1/*
2 * Copyright 2014, Haiku, inc.
3 * Distributed under the terms of the MIT licence
4 */
5
6
7#include "CookieTest.h"
8
9
10#include <cstdlib>
11#include <cstring>
12#include <cstdio>
13
14#include <NetworkCookieJar.h>
15
16#include <cppunit/TestCaller.h>
17#include <cppunit/TestSuite.h>
18
19
20using namespace BPrivate::Network;
21
22
23CookieTest::CookieTest()
24{
25}
26
27
28CookieTest::~CookieTest()
29{
30}
31
32
33// #pragma mark - Positive functionality tests
34
35
36void
37CookieTest::SimpleTest()
38{
39	BNetworkCookieJar jar;
40	char buffer[256];
41	BUrl url("http://www.chipchapin.com/WebTools/cookietest.php");
42
43	time_t t = time(NULL) + 6400; // Cookies expire in 1h45
44	struct tm* now = gmtime(&t);
45	status_t result;
46
47	// Add various cookies
48	result = jar.AddCookie("testcookie1=present;", url);
49	CPPUNIT_ASSERT(result == B_OK);
50
51	strftime(buffer, sizeof(buffer),
52		"testcookie2=present; expires=%A, %d-%b-%Y %H:%M:%S", now);
53	result = jar.AddCookie(buffer, url);
54	CPPUNIT_ASSERT(result == B_OK);
55
56	strftime(buffer, sizeof(buffer),
57		"testcookie3=present; expires=%A, %d-%b-%Y %H:%M:%S; path=/", now);
58	result = jar.AddCookie(buffer, url);
59	CPPUNIT_ASSERT(result == B_OK);
60
61	t += 3200; // expire in 2h40
62	now = gmtime(&t);
63	strftime(buffer, sizeof(buffer),
64		"testcookie4=present; path=/; domain=www.chipchapin.com; "
65		"expires=%A, %d-%b-%Y %H:%M:%S", now);
66	result = jar.AddCookie(buffer, url);
67	CPPUNIT_ASSERT(result == B_OK);
68
69	// Now check they were all properly added
70	BNetworkCookieJar::Iterator it = jar.GetIterator();
71
72	int count = 0;
73	while (it.HasNext()) {
74		count ++;
75		it.Next();
76	}
77
78	CPPUNIT_ASSERT_EQUAL(4, count);
79}
80
81
82void
83CookieTest::StandardTest()
84{
85	BNetworkCookieJar jar;
86	status_t result;
87
88	BUrl url("http://testsuites.opera.com/cookies/001.php");
89	result = jar.AddCookie("001=1", url);
90	CPPUNIT_ASSERT(result == B_OK);
91
92	url.SetUrlString("http://testsuites.opera.com/cookies/001-1.php");
93	const BNetworkCookie* cookie = _GetCookie(jar, url, "001");
94
95	CPPUNIT_ASSERT(cookie != NULL);
96
97	CPPUNIT_ASSERT(cookie->HasValue());
98	CPPUNIT_ASSERT_EQUAL(BString("1"), cookie->Value());
99	CPPUNIT_ASSERT(cookie->IsSessionCookie());
100	CPPUNIT_ASSERT(cookie->ShouldDeleteAtExit());
101
102	CPPUNIT_ASSERT(!cookie->HasExpirationDate());
103	CPPUNIT_ASSERT(!cookie->Secure());
104	CPPUNIT_ASSERT(!cookie->HttpOnly());
105}
106
107
108void
109CookieTest::ExpireTest()
110{
111	BNetworkCookieJar jar;
112	status_t result;
113
114	BUrl url("http://testsuites.opera.com/cookies/003.php");
115	result = jar.AddCookie("003=1", url);
116	CPPUNIT_ASSERT(result == B_OK);
117
118	url.SetUrlString("http://testsuites.opera.com/cookies/003-1.php");
119	result = jar.AddCookie("003=1; expires=Thu, 01-Jan-1970 00:00:10 GMT", url);
120	CPPUNIT_ASSERT(result == B_OK);
121
122	url.SetUrlString("http://testsuites.opera.com/cookies/003-2.php");
123	// The cookie should be expired.
124	CPPUNIT_ASSERT(_GetCookie(jar, url, "003") == NULL);
125}
126
127
128void
129CookieTest::PathTest()
130{
131	BNetworkCookieJar jar;
132	status_t result;
133
134	BUrl url("http://testsuites.opera.com/cookies/004/004.php");
135	result = jar.AddCookie("004=1", url);
136	CPPUNIT_ASSERT(result == B_OK);
137
138	// Page in the same path can access the cookie
139	url.SetUrlString("http://testsuites.opera.com/cookies/004/004-1.php");
140	CPPUNIT_ASSERT(_GetCookie(jar, url, "004") != NULL);
141
142	// Page in parent directory cannot access the cookie
143	url.SetUrlString("http://testsuites.opera.com/cookies/004-2.php");
144	CPPUNIT_ASSERT(_GetCookie(jar, url, "004") == NULL);
145}
146
147
148void
149CookieTest::MaxSizeTest()
150{
151	BNetworkCookieJar jar;
152	status_t result;
153
154	BUrl url("http://testsuites.opera.com/cookies/006.php");
155	BString cookieString("006=");
156	for (int i = 0; i < 128; i++) {
157		cookieString << "00xxxxxxxxxxxxxx16xxxxxxxxxxxxxx";
158	}
159	result = jar.AddCookie(cookieString, url);
160	CPPUNIT_ASSERT(result == B_OK);
161
162	url.SetUrlString("http://testsuites.opera.com/cookies/006-1.php");
163	const BNetworkCookie* cookie = _GetCookie(jar, url, "006");
164	CPPUNIT_ASSERT(cookie != NULL);
165	CPPUNIT_ASSERT(cookie->Value().Length() == 4096);
166}
167
168
169void
170CookieTest::MaxNumberTest()
171{
172	BNetworkCookieJar jar;
173	status_t result;
174
175	BUrl url("http://testsuites.opera.com/cookies/007.php");
176	BString cookieString;
177
178	for (int i = 1; i <= 20; i++)
179	{
180		cookieString.SetToFormat("007-%d=1", i);
181		result = jar.AddCookie(cookieString, url);
182		CPPUNIT_ASSERT(result == B_OK);
183	}
184
185	url.SetUrlString("http://testsuites.opera.com/cookies/007-1.php");
186	for (int i = 1; i <= 20; i++)
187	{
188		cookieString.SetToFormat("007-%d", i);
189		const BNetworkCookie* cookie = _GetCookie(jar, url, cookieString.String());
190		CPPUNIT_ASSERT(cookie != NULL);
191		CPPUNIT_ASSERT(cookie->Value() == "1");
192	}
193}
194
195
196void
197CookieTest::UpdateTest()
198{
199	BNetworkCookieJar jar;
200	status_t result;
201
202	BUrl url("http://testsuites.opera.com/cookies/008.php");
203	result = jar.AddCookie("008=1", url);
204	CPPUNIT_ASSERT(result == B_OK);
205
206	url.SetUrlString("http://testsuites.opera.com/cookies/008-1.php");
207	result = jar.AddCookie("008=2", url);
208	CPPUNIT_ASSERT(result == B_OK);
209
210	url.SetUrlString("http://testsuites.opera.com/cookies/008-2.php");
211	const BNetworkCookie* cookie = _GetCookie(jar, url, "008");
212	CPPUNIT_ASSERT(cookie != NULL);
213	CPPUNIT_ASSERT(cookie->Value() == "2");
214}
215
216
217void
218CookieTest::HttpOnlyTest()
219{
220	BNetworkCookieJar jar;
221	status_t result;
222
223	BUrl url("http://testsuites.opera.com/cookies/010.php");
224	result = jar.AddCookie("010=1; Httponly; Max-age=1", url);
225	CPPUNIT_ASSERT(result == B_OK);
226
227	url.SetUrlString("http://testsuites.opera.com/cookies/010-1.php");
228	const BNetworkCookie* cookie = _GetCookie(jar, url, "010");
229	CPPUNIT_ASSERT(cookie != NULL);
230	CPPUNIT_ASSERT(cookie->Value() == "1");
231	CPPUNIT_ASSERT(cookie->HttpOnly());
232	CPPUNIT_ASSERT(cookie->HasExpirationDate());
233}
234
235
236void
237CookieTest::EncodingTest()
238{
239	BNetworkCookieJar jar;
240	status_t result;
241
242	BUrl url("http://testsuites.opera.com/cookies/011.php");
243	result = jar.AddCookie("011=UTF-8 \303\246\303\270\303\245 \346\227\245\346\234\254;", url);
244	CPPUNIT_ASSERT(result == B_OK);
245
246	url.SetUrlString("http://testsuites.opera.com/cookies/011-1.php");
247	const BNetworkCookie* cookie = _GetCookie(jar, url, "011");
248	CPPUNIT_ASSERT(cookie != NULL);
249	CPPUNIT_ASSERT_EQUAL(
250		BString("UTF-8 \303\246\303\270\303\245 \346\227\245\346\234\254"),
251		cookie->Value());
252}
253
254
255void
256CookieTest::DomainTest()
257{
258	BNetworkCookieJar jar;
259	status_t result;
260
261	BUrl url("http://testsuites.opera.com/cookies/012.php");
262	result = jar.AddCookie("012-1=1; Domain=\"opera.com\"", url);
263	CPPUNIT_ASSERT(result == B_OK);
264	result = jar.AddCookie("012-1=1; Domain=\"example.com\"", url);
265	CPPUNIT_ASSERT(result != B_OK);
266
267	url.SetUrlString("http://testsuites.opera.com/cookies/012-1.php");
268	const BNetworkCookie* cookie = _GetCookie(jar, url, "012-1");
269	CPPUNIT_ASSERT(cookie != NULL);
270	CPPUNIT_ASSERT(cookie->Value() == "1");
271
272	cookie = _GetCookie(jar, url, "012-2");
273	CPPUNIT_ASSERT(cookie == NULL);
274}
275
276
277void
278CookieTest::PersistantTest()
279{
280	BNetworkCookieJar jar;
281	status_t result;
282	char buffer[256];
283
284	time_t t = time(NULL) + 100000;
285	struct tm* now = gmtime(&t);
286
287	NextSubTest();
288
289	BUrl url("http://testsuites.opera.com/cookies/013.php");
290
291	strftime(buffer, sizeof(buffer),
292		"013-1=1; Expires=%a, %d-%b-%Y %H:%M:%S", now);
293	result = jar.AddCookie(buffer, url);
294	CPPUNIT_ASSERT(result == B_OK);
295
296	result = jar.AddCookie("013-2=1", url);
297	CPPUNIT_ASSERT(result == B_OK);
298
299	NextSubTest();
300
301	url.SetUrlString("http://testsuites.opera.com/cookies/013-1.php");
302
303	const BNetworkCookie* cookie = _GetCookie(jar, url, "013-1");
304	CPPUNIT_ASSERT(cookie != NULL);
305	CPPUNIT_ASSERT(cookie->Value() == "1");
306	CPPUNIT_ASSERT(cookie->HasExpirationDate());
307
308	cookie = _GetCookie(jar, url, "013-2");
309	CPPUNIT_ASSERT(cookie != NULL);
310	CPPUNIT_ASSERT(cookie->Value() == "1");
311	CPPUNIT_ASSERT(!cookie->HasExpirationDate());
312
313	// Simulate exit
314	jar.PurgeForExit();
315
316	NextSubTest();
317
318	cookie = _GetCookie(jar, url, "013-1");
319	CPPUNIT_ASSERT(cookie != NULL);
320	CPPUNIT_ASSERT(cookie->Value() == "1");
321	CPPUNIT_ASSERT(cookie->HasExpirationDate());
322
323	cookie = _GetCookie(jar, url, "013-2");
324	CPPUNIT_ASSERT(cookie == NULL);
325}
326
327
328void
329CookieTest::OverwriteTest()
330{
331	BNetworkCookieJar jar;
332	status_t result;
333
334	BUrl url("http://testsuites.opera.com/cookies/015/015.php");
335	result = jar.AddCookie("015-01=1", url);
336	CPPUNIT_ASSERT(result == B_OK);
337
338	url.SetUrlString("http://testsuites.opera.com/cookies/015-1.php");
339	result = jar.AddCookie("015-01=1", url);
340	result = jar.AddCookie("015-02=1", url);
341	CPPUNIT_ASSERT(result == B_OK);
342
343	url.SetUrlString("http://testsuites.opera.com/cookies/015/015-2.php");
344	result = jar.AddCookie("015-01=1", url);
345	result = jar.AddCookie("015-02=1", url);
346	CPPUNIT_ASSERT(result == B_OK);
347
348	url.SetUrlString("http://testsuites.opera.com/cookies/015/015-3.php");
349	BNetworkCookieJar::UrlIterator it = jar.GetUrlIterator(url);
350	int count = 0;
351
352	while (it.HasNext()) {
353		it.Next();
354		count++;
355	}
356
357	CPPUNIT_ASSERT_EQUAL(4, count);
358}
359
360
361void
362CookieTest::OrderTest()
363{
364	BNetworkCookieJar jar;
365	status_t result;
366
367	BUrl url("http://testsuites.opera.com/cookies/016.php");
368	result = jar.AddCookie("016-01=1", url);
369	CPPUNIT_ASSERT(result == B_OK);
370
371	url.SetUrlString("http://testsuites.opera.com/cookies/016/016-1.php");
372	result = jar.AddCookie("016-02=1", url);
373	CPPUNIT_ASSERT(result == B_OK);
374
375	url.SetUrlString("http://testsuites.opera.com/cookies/016/016-2.php");
376	BNetworkCookieJar::UrlIterator it = jar.GetUrlIterator(url);
377	int count = 0;
378
379	// Check that the cookie with the most specific path is sent first
380	while (it.HasNext()) {
381		const BNetworkCookie* cookie = it.Next();
382		switch(count)
383		{
384			case 0:
385				CPPUNIT_ASSERT_EQUAL(BString("016-02"), cookie->Name());
386				break;
387			case 1:
388				CPPUNIT_ASSERT_EQUAL(BString("016-01"), cookie->Name());
389				break;
390		}
391		count++;
392	}
393
394	CPPUNIT_ASSERT_EQUAL(2, count);
395}
396
397
398// #pragma mark - Error handling and extended tests
399
400
401void
402CookieTest::ExpireParsingTest()
403{
404	BUrl url("http://testsuites.opera.com/cookies/301.php");
405	BNetworkCookie cookie;
406	status_t result;
407
408	BString bigData("301-16=1; Expires=\"");
409	for (int i = 0; i < 1500; i++)
410		bigData << "abcdefghijklmnopqrstuvwxyz";
411	bigData << "\";";
412
413	struct Test {
414		const char* cookieString;
415		bool canParse;
416		bool sessionOnly;
417		bool expired;
418	};
419
420	Test tests[] = {
421		{ "301-01=1; Expires=\"notAValidValue\";",
422			true, true, false }, // Obviously invalid date
423		{ "301-02=1; Expires=\"Wed, 08-Nov-2035 01:04:33\";",
424			true, false, false }, // Valid date
425		{ "301-03=1; Expires=\"Tue, 19-Jan-2038 03:14:06\";",
426			true, false, false }, // Valid date, after year 2038 time_t overflow
427		{ "301-04=1; Expires=\"Fri, 13-Dec-1901 20:45:51\";",
428			true, false, true }, // Valid date, in the past
429		{ "301-05=1; Expires=\"Thu, 33-Nov-2035 01:04:33\";",
430			true, true, false }, // Invalid day
431		{ "301-06=1; Expires=\"Wed, 08-Siz-2035 01:04:33\";",
432			true, true, false }, // Invalid month
433		{ "301-07=1; Expires=\"Wed, 08-Nov-9035 01:04:33\";",
434			true, false, false }, // Very far in the future
435			// NOTE: Opera testsuite considers it should be a session cookie.
436		{ "301-08=1; Expires=\"Wed, 08-Nov-2035 75:04:33\";",
437			true, true, false }, // Invalid hour
438		{ "301-09=1; Expires=\"Wed, 08-Nov-2035 01:75:33\";",
439			true, true, false }, // Invalid minute
440		{ "301-10=1; Expires=\"Wed, 08-Nov-2035 01:04:75\";",
441			true, true, false }, // Invalid second
442		{ "301-11=1; Expires=\"XXX, 08-Nov-2035 01:04:33\";",
443			true, true, false }, // Invalid weekday
444		{ "301-12=1; Expires=\"Thu, XX-Nov-2035 01:04:33\";",
445			true, true, false }, // Non-digit day
446		{ "301-13=1; Expires=\"Thu, 08-Nov-XXXX 01:04:33\";",
447			true, true, false }, // Non-digit year
448		{ "301-14=1; Expires=\"Thu, 08-Nov-2035 XX:XX:33\";",
449			true, true, false }, // Non-digit hour and minute
450		{ "301-15=1; Expires=\"Thu, 08-Nov-2035 01:04:XX\";",
451			true, true, false }, // Non-digit second
452		{ bigData.String(),
453			true, true, false }, // Very long invalid string
454			// NOTE: Opera doesn't register the cookie at all.
455		{ "301-17=1; Expires=\"Thu, 99999-Nov-2035 01:04:33\";",
456			true, true, false }, // Day with many digits
457		{ "301-18=1; Expires=\"Thu, 25-Nov-99999 01:04:33\";",
458			true, true, false }, // Year with many digits
459			// NOTE: Opera tests 301-17 twice and misses this test.
460		{ "301-19=1; Expires=\"Thu, 25-Nov-2035 99999:04:33\";",
461			true, true, false }, // Hour with many digits
462		{ "301-20=1; Expires=\"Thu, 25-Nov-2035 01:99999:33\";",
463			true, true, false }, // Minute with many digits
464		{ "301-21=1; Expires=\"Thu, 25-Nov-2035 01:04:99999\";",
465			true, true, false }, // Second with many digits
466		{ "301-22=1; Expires=\"99999999999999999999\";",
467			true, true, false }, // Huge number
468		{ "301-23=1; Expires=\"Fri, 13-Dec-101 20:45:51\";",
469			true, false, true }, // Very far in the past
470		{ "301-24=1; EXPiReS=\"Wed, 08-Nov-2035 01:04:33\";",
471			true, false, false }, // Case insensitive key parsing
472		{ "301-25=1; Expires=Wed, 08-Nov-2035 01:04:33\";",
473			true, true, false }, // Missing opening quote
474			// NOTE: unlike Opera, we accept badly quoted values for cookie
475			// attributes. This allows to handle unquoted values from the early
476			// cookie spec properly. However, a date with a quote inside it
477			// should not be accepted, so this will be a session cookie.
478		{ "301-26=1; Expires=\"Wed, 08-Nov-2035 01:04:33;",
479			true, true, false }, // Missing closing quote
480		{ "301-27=1; Expires:\"Wed, 08-Nov-2035 01:04:33\";",
481			true, true, false }, // Uses : instead of =
482			// NOTE: unlike Opera, we allow this as a cookie with a strange
483			// name and no value
484		{ "301-28=1; Expires;=\"Wed, 08-Nov-2035 01:04:33\";",
485			true, true, false }, // Extra ; after name
486		{ "301-29=1; Expired=\"Wed, 08-Nov-2035 01:04:33\";",
487			true, true, false }, // Expired instead of Expires
488		{ "301-30=1; Expires=\"Wed; 08-Nov-2035 01:04:33\";",
489			true, true, false }, // ; in value
490		{ "301-31=1; Expires=\"Wed,\\r\\n 08-Nov-2035 01:04:33\";",
491			true, true, false }, // escaped newline in value
492			// NOTE: Only here for completeness. This is what the Opera
493			// testsuite sends in test 31, but I suspect they were trying to
494			// test the following case.
495		{ "301-31b=1; Expires=\"Wed,\r\n 08-Nov-2035 01:04:33\";",
496			true, false, false }, // newline in value
497			// NOTE: This can't really happen when we get cookies from HTTP
498			// headers. It could happen when the cookie is set from meta html
499			// tags or from JS.
500		{ "301-32=1; expires=Mon, 31-Oct-2035 08:08:40 GMT;",
501			true, false, false }, // Wrong weekday
502		{ "301-33=1; expires=Tue, 19-Oct-66 07:08:40;",
503			true, false, false }, // RFC1036 format with 2 digit year
504		{ "301-34=1; expires=Sat, 21-Oct-56 07:08:40 GMT;",
505			true, false, false }, // RFC1036 format with 2 digit year
506	};
507
508	for (unsigned int i = 0; i < sizeof(tests) / sizeof(Test); i++)
509	{
510		NextSubTest();
511
512		result = cookie.ParseCookieString(tests[i].cookieString, url);
513		CPPUNIT_ASSERT_EQUAL_MESSAGE("Cookie can be parsed",
514			tests[i].canParse, result == B_OK);
515		CPPUNIT_ASSERT_EQUAL_MESSAGE("Cookie is session only",
516			tests[i].sessionOnly, cookie.IsSessionCookie());
517		CPPUNIT_ASSERT_EQUAL_MESSAGE("Cookie has expired",
518			tests[i].expired, cookie.ShouldDeleteNow());
519	}
520}
521
522
523void
524CookieTest::PathMatchingTest()
525{
526	const BUrl url("http://testsuites.opera.com/cookies/302/302.php");
527	const BUrl url1("http://testsuites.opera.com/cookies/302-5.php");
528	const BUrl url2("http://testsuites.opera.com/cookies/302/302-3.php");
529	const BUrl url3("http://testsuites.opera.com/cookies/302/sub/302-4.php");
530	const BUrl url4("http://testsuites.opera.com/cookies/302-2/302-6.php");
531	BNetworkCookie cookie;
532	status_t result;
533
534	BString bigData("302-24=1; Path=\"/cookies/302/");
535	for (int i = 0; i < 1500; i++)
536		bigData << "abcdefghijklmnopqrstuvwxyz";
537	bigData << "\";";
538
539	struct Test {
540		const char* cookieString;
541		bool canSet;
542		bool url1;
543		bool url2;
544		bool url3;
545		bool url4;
546	};
547
548	const Test tests[] = {
549		{ "302-01=1; Path=\"/\"",
550			true, true, true, true, true },
551		{ "302-02=1; Path=\"/cookies\"",
552			true, true, true, true, true },
553		{ "302-03=1; Path=\"/cookies/\"",
554			true, true, true, true, true },
555		{ "302-04=1; Path=\"/cookies/302\"",
556			true, false, true, true, true },
557		{ "302-05=1; Path=\"/cookies/302/\"",
558			true, false, true, true, false },
559		{ "302-06=1; Path=\"/cookies/302/.\"",
560			false, false, false, false, false },
561		{ "302-07=1; Path=\"/cookies/302/../302\"",
562			false, false, false, false, false },
563		{ "302-08=1; Path=\"/cookies/302/../302-2\"",
564			false, false, false, false, false },
565		{ "302-09=1; Path=\"/side\"",
566			false, false, false, false, false },
567		{ "302-10=1; Path=\"/cookies/302-2\"",
568			true, false, false, false, true },
569		{ "302-11=1; Path=\"/cookies/302-2/\"",
570			true, false, false, false, true },
571		{ "302-12=1; Path=\"sub\"",
572			false, false, false, false, false },
573		{ "302-13=1; Path=\"sub/\"",
574			false, false, false, false, false },
575		{ "302-14=1; Path=\".\"",
576			false, false, false, false, false },
577		{ "302-15=1; Path=\"/cookies/302/sub\"",
578			true, false, false, true, false },
579		{ "302-16=1; Path=\"/cookies/302/sub/\"",
580			true, false, false, true, false },
581		{ "302-17=1; Path=\"/cookies/302/sub/..\"",
582			false, false, false, false, false },
583		{ "302-18=1; Path=/",
584			true, true, true, true, true },
585		{ "302-19=1; Path=/ /",
586			false, false, false, false, false },
587		{ "302-20=1; Path=\"/",
588			false, false, false, false, false },
589		{ "302-21=1; Path=/\"",
590			false, false, false, false, false },
591		{ "302-22=1; Path=\\/",
592			false, false, false, false, false },
593		{ "302-23=1; Path=\n/",
594			false, false, false, false, false },
595		{ bigData,
596			false, false, false, false, false },
597	};
598
599	for (unsigned int i = 0; i < sizeof(tests) / sizeof(Test); i++)
600	{
601		NextSubTest();
602
603		result = cookie.ParseCookieString(tests[i].cookieString, url);
604
605		CPPUNIT_ASSERT_EQUAL_MESSAGE("Allowed to set cookie",
606			tests[i].canSet, result == B_OK);
607		CPPUNIT_ASSERT_EQUAL(tests[i].url1, cookie.IsValidForUrl(url1));
608		CPPUNIT_ASSERT_EQUAL(tests[i].url2, cookie.IsValidForUrl(url2));
609		CPPUNIT_ASSERT_EQUAL(tests[i].url3, cookie.IsValidForUrl(url3));
610		CPPUNIT_ASSERT_EQUAL(tests[i].url4, cookie.IsValidForUrl(url4));
611	}
612}
613
614
615void
616CookieTest::DomainMatchingTest()
617{
618	const BUrl setter("http://testsuites.opera.com/cookies/304.php");
619	const BUrl getter("http://testsuites.opera.com/cookies/304-1.php");
620
621	BString bigData("304-12=1; Domain=\"");
622	for (int i = 0; i < 1500; i++)
623		bigData << "abcdefghijklmnopqrstuvwxyz";
624	bigData << "\";";
625
626	struct Test {
627		const char* cookieString;
628		bool shouldMatch;
629	};
630
631	const Test tests[] = {
632		{ "304-01=1; Domain=\"opera.com\"", true },
633		{ "304-02=1; Domain=\".opera.com\"", true },
634		{ "304-03=1; Domain=\".com\"", false },
635		{ "304-04=1; Domain=\"pera.com\"", false },
636		{ "304-05=1; Domain=\"?pera.com\"", false },
637		{ "304-06=1; Domain=\"*.opera.com\"", false },
638		{ "304-07=1; Domain=\"300.300.300.300\"", false },
639		{ "304-08=1; Domain=\"123456123456123456\"", false },
640		{ "304-09=1; Domain=\"/opera.com\"", false },
641		{ "304-10=1; Domain=\"opera.com/\"", false },
642		{ "304-11=1; Domain=\"example.com\"", false },
643		{ bigData, false },
644		{ "304-13=1; Domain=\"opera com\"", false },
645		{ "304-14=1; Domain=opera.com\"", false },
646		{ "304-15=1; Domain=\"opera.com", false },
647		{ "304-16=1; Domain=opera.com", true },
648		{ "304-17=1; Domain=\"*.com\"", false },
649		{ "304-18=1; Domain=\"*opera.com\"", false },
650		{ "304-19=1; Domain=\"*pera.com\"", false },
651		{ "304-20=1; Domain=\"\"", false },
652		{ "304-21=1; Domain=\"\346\227\245\346\234\254\"", false },
653		{ "304-22=1; Domain=\"OPERA.COM\"", true },
654		{ "304-23=1; Domain=\"195.189.143.182\"", false },
655	};
656
657	for (unsigned int i = 0; i < sizeof(tests) / sizeof(Test); i++)
658	{
659		NextSubTest();
660
661		BNetworkCookie cookie(tests[i].cookieString, setter);
662		CPPUNIT_ASSERT_EQUAL(tests[i].shouldMatch,
663			cookie.IsValidForUrl(getter));
664	}
665}
666
667
668void
669CookieTest::MaxAgeParsingTest()
670{
671	const BUrl setter("http://testsuites.opera.com/cookies/305.php");
672
673	BString bigData("305-12=1; Max-Age=\"");
674	for (int i = 0; i < 1500; i++)
675		bigData << "abcdefghijklmnopqrstuvwxyz";
676	bigData << "\";";
677
678	struct Test {
679		const char* cookieString;
680		bool expired;
681	};
682
683	const Test tests[] = {
684		{ "305-01=1; Max-Age=\"notAValidValue\"", true },
685		{ "305-02=1; Max-Age=\"Wed, 08-Nov-2035 01:04:33\"", true },
686		{ "305-03=1; Max-Age=\"0\"", true },
687		{ "305-04=1; Max-Age=\"1\"", false },
688		{ "305-05=1; Max-Age=\"10000\"", false },
689		{ "305-06=1; Max-Age=\"-1\"", true },
690		{ "305-07=1; Max-Age=\"0.5\"", true },
691		{ "305-08=1; Max-Age=\"9999999999999999999999999\"", false },
692		{ "305-09=1; Max-Age=\"-9999999999999999999999999\"", true },
693		{ "305-10=1; Max-Age=\"+10000\"", false },
694		{ "305-11=1; Max-Age=\"Fri, 13-Dec-1901 20:45:52\"", true },
695		{ bigData, true },
696		{ "305-13=1; Max-Age=\"0+10000\"", true },
697		{ "305-14=1; Max-Age=\"10000+0\"", true },
698		{ "305-15=1; Max-Age=10000\"", true },
699		{ "305-16=1; Max-Age=\"10000", true },
700		{ "305-17=1; Max-Age=10000", false },
701		{ "305-18=1; Max-Age=100\"00", true },
702	};
703
704	for (unsigned int i = 0; i < sizeof(tests) / sizeof(Test); i++)
705	{
706		NextSubTest();
707
708		BNetworkCookie cookie(tests[i].cookieString, setter);
709		CPPUNIT_ASSERT_EQUAL(tests[i].expired, cookie.ShouldDeleteNow());
710	}
711}
712
713
714void
715CookieTest::ExplodeTest()
716{
717	struct Test {
718		const char* cookieString;
719		const char*	url;
720		struct
721		{
722			bool		valid;
723			const char* name;
724			const char* value;
725			const char* domain;
726			const char* path;
727			bool 		secure;
728			bool 		httponly;
729			bool		session;
730			BDateTime	expire;
731		} expected;
732	};
733
734	Test tests[] = {
735	//     Cookie string      URL
736	//     ------------- -------------
737	//		   Valid     Name     Value	       Domain         Path      Secure  HttpOnly Session  Expiration
738	//       --------- -------- --------- ----------------- ---------  -------- -------- -------  ----------
739		// Normal cookies
740		{ "name=value", "http://www.example.com/path/path",
741			{  true,    "name",  "value", "www.example.com", "/path",   false,   false,   true,   BDateTime() } },
742		{ "name=value; domain=example.com; path=/; secure", "http://www.example.com/path/path",
743			{  true,    "name",  "value",   "example.com",   "/"    ,   true,    false,   true,   BDateTime() } },
744		{ "name=value; httponly; secure", "http://www.example.com/path/path",
745			{  true,    "name",  "value", "www.example.com", "/path",   true,    true,    true,   BDateTime() } },
746		{ "name=value; expires=Wed, 20-Feb-2013 20:00:00 UTC", "http://www.example.com/path/path",
747			{  true,    "name",  "value", "www.example.com", "/path",   false,   false,   false,
748				BDateTime(BDate(2013, 2, 20), BTime(20, 0, 0, 0)) } },
749		// Valid cookie with bad form
750		{ "name=  ;  domain   =example.com  ;path=/;  secure = yup  ; blahblah ;)", "http://www.example.com/path/path",
751			{  true,    "name",  "",   "example.com",   "/"    ,   true,    false,   true,   BDateTime() } },
752		// Invalid path
753		{ "name=value; path=invalid", "http://www.example.com/path/path",
754			{  false,    "name",  "value", "www.example.com", "/path",   false,   false,   true,   BDateTime() } },
755		// Setting for other subdomain (invalid)
756		{ "name=value; domain=subdomain.example.com", "http://www.example.com/path/path",
757			{  false,   "name",  "value", "www.example.com", "/path",   false,   false,   true,   BDateTime() } },
758		// Various invalid cookies
759		{ "name", "http://www.example.com/path/path",
760			{  false,   "name",  "value", "www.example.com", "/path",   false,   false,   true,   BDateTime() } },
761		{ "; domain=example.com", "http://www.example.com/path/path",
762			{  false,   "name",  "value", "www.example.com", "/path",   false,   false,   true,   BDateTime() } }
763	};
764
765	BNetworkCookie cookie;
766
767	for (uint32 i = 0; i < (sizeof(tests) / sizeof(Test)); i++) {
768		NextSubTest();
769
770		BUrl url(tests[i].url);
771		cookie.ParseCookieString(tests[i].cookieString, url);
772
773		CPPUNIT_ASSERT(tests[i].expected.valid == cookie.IsValid());
774
775		if (!tests[i].expected.valid)
776			continue;
777
778		CPPUNIT_ASSERT_EQUAL(BString(tests[i].expected.name), cookie.Name());
779		CPPUNIT_ASSERT_EQUAL(BString(tests[i].expected.value), cookie.Value());
780		CPPUNIT_ASSERT_EQUAL(BString(tests[i].expected.domain),
781			cookie.Domain());
782		CPPUNIT_ASSERT_EQUAL(BString(tests[i].expected.path), cookie.Path());
783		CPPUNIT_ASSERT(tests[i].expected.secure == cookie.Secure());
784		CPPUNIT_ASSERT(tests[i].expected.httponly == cookie.HttpOnly());
785		CPPUNIT_ASSERT(tests[i].expected.session == cookie.IsSessionCookie());
786
787		if (!cookie.IsSessionCookie())
788			CPPUNIT_ASSERT_EQUAL(tests[i].expected.expire.Time_t(),
789				cookie.ExpirationDate());
790	}
791}
792
793
794/* static */ void
795CookieTest::AddTests(BTestSuite& parent)
796{
797	CppUnit::TestSuite& suite = *new CppUnit::TestSuite("CookieTest");
798
799	suite.addTest(new CppUnit::TestCaller<CookieTest>("CookieTest::SimpleTest",
800		&CookieTest::SimpleTest));
801	suite.addTest(new CppUnit::TestCaller<CookieTest>(
802		"CookieTest::StandardTest", &CookieTest::StandardTest));
803	suite.addTest(new CppUnit::TestCaller<CookieTest>("CookieTest::ExpireTest",
804		&CookieTest::ExpireTest));
805	suite.addTest(new CppUnit::TestCaller<CookieTest>("CookieTest::PathTest",
806		&CookieTest::PathTest));
807	suite.addTest(new CppUnit::TestCaller<CookieTest>("CookieTest::MaxSizeTest",
808		&CookieTest::MaxSizeTest));
809	suite.addTest(new CppUnit::TestCaller<CookieTest>(
810		"CookieTest::MaxNumberTest", &CookieTest::MaxNumberTest));
811	suite.addTest(new CppUnit::TestCaller<CookieTest>("CookieTest::UpdateTest",
812		&CookieTest::UpdateTest));
813	suite.addTest(new CppUnit::TestCaller<CookieTest>(
814		"CookieTest::HttpOnlyTest", &CookieTest::HttpOnlyTest));
815	suite.addTest(new CppUnit::TestCaller<CookieTest>(
816		"CookieTest::EncodingTest", &CookieTest::EncodingTest));
817	suite.addTest(new CppUnit::TestCaller<CookieTest>("CookieTest::DomainTest",
818		&CookieTest::DomainTest));
819	suite.addTest(new CppUnit::TestCaller<CookieTest>(
820		"CookieTest::PersistantTest", &CookieTest::PersistantTest));
821	suite.addTest(new CppUnit::TestCaller<CookieTest>(
822		"CookieTest::OverwriteTest", &CookieTest::OverwriteTest));
823	suite.addTest(new CppUnit::TestCaller<CookieTest>(
824		"CookieTest::OrderTest", &CookieTest::OrderTest));
825
826	suite.addTest(new CppUnit::TestCaller<CookieTest>(
827		"CookieTest::ExpireParsingTest", &CookieTest::ExpireParsingTest));
828	suite.addTest(new CppUnit::TestCaller<CookieTest>(
829		"CookieTest::PathMatchingTest", &CookieTest::PathMatchingTest));
830	suite.addTest(new CppUnit::TestCaller<CookieTest>(
831		"CookieTest::DomainMatchingTest", &CookieTest::DomainMatchingTest));
832	suite.addTest(new CppUnit::TestCaller<CookieTest>(
833		"CookieTest::MaxAgeParsingTest", &CookieTest::MaxAgeParsingTest));
834	suite.addTest(new CppUnit::TestCaller<CookieTest>(
835		"CookieTest::ExplodeTest", &CookieTest::ExplodeTest));
836
837	parent.addTest("CookieTest", &suite);
838}
839
840
841const BNetworkCookie*
842CookieTest::_GetCookie(BNetworkCookieJar& jar, const BUrl& url,
843	const char* name)
844{
845	BNetworkCookieJar::UrlIterator it = jar.GetUrlIterator(url);
846	const BNetworkCookie* result = NULL;
847
848	while (it.HasNext()) {
849		const BNetworkCookie* cookie = it.Next();
850		if (cookie->Name() == name) {
851			// Make sure the cookie is found only once.
852			CPPUNIT_ASSERT(result == NULL);
853
854			result = cookie;
855		}
856	}
857
858	return result;
859}
860