require "vnd.dovecot.testsuite";

/*
 * ## RFC 5228, Section 5.1. Test address (page 26) ##
 */

/*
 * TEST: Basic functionionality
 */

/* "The "address" test matches Internet addresses in structured headers
 *  that contain addresses.  It returns true if any header contains any
 *  key in the specified part of the address, as modified by the
 *  comparator and the match keyword.  Whether there are other addresses
 *  present in the header doesn't affect this test; this test does not
 *  provide any way to determine whether an address is the only address
 *  in a header.
 *
 *  Like envelope and header, this test returns true if any combination
 *  of the header-list and key-list arguments match and returns false
 *  otherwise.
 * "
 */

test_set "message" text:
From: stephan@example.com
To: nico@nl.example.com, harry@de.example.com
cc: Timo <tss(no spam)@fi.iki>
Subject: Frobnitzm

Test.
.
;

test "Basic functionality" {
	/* Must match */
	if not address :contains ["to", "from"] "harry" {
		test_fail "failed to match address (1)";
	}

	if not address :contains ["to", "from"] "de.example" {
		test_fail "failed to match address (2)";
	}

	if not address :matches "to" "*@*.example.com" {
		test_fail "failed to match address (3)";
	}

	if not address :is "to" "harry@de.example.com" {
		test_fail "failed to match address (4)";
	}

	/* Must not match */
	if address :is ["to", "from"] "nonsense@example.com" {
		test_fail "matches erroneous address";
	}

	/* Match first key */
	if not address :contains ["to"] ["nico", "fred", "henk"] {
		test_fail "failed to match first key";
	}

	/* Match second key */
	if not address :contains ["to"] ["fred", "nico", "henk"] {
		test_fail "failed to match second key";
	}

	/* Match last key */
	if not address :contains ["to"] ["fred", "henk", "nico"] {
		test_fail "failed to match last key";
	}

	/* First header */
	if not address :contains ["to", "from"] ["fred", "nico", "henk"] {
		test_fail "failed to match first header";
	}

	/* Second header */
	if not address :contains ["from", "to"] ["fred", "nico", "henk"] {
		test_fail "failed to match second header";
	}

	/* Comment */
	if not address :is "cc" "tss@fi.iki" {
		test_fail "failed to ignore comment in address";
	}
}

/*
 * TEST: Case-sensitivity
 */

/* "Internet email addresses [RFC 2822] have the somewhat awkward characteristic
 *  that the local-part to the left of the at-sign is considered case sensitive,
 *  and the domain-part to the right of the at-sign is case insensitive. The
 *  "address" command does not deal with this itself, but provides the
 *  ADDRESS-PART argument for allowing users to deal with it.
 * "
 */

test_set "message" text:
From: stephan@example.com
To: Nico@nl.example.com, harry@DE.EXAMPLE.COM
Subject: Case-sensitivity

Test.
.
;


test "Case-sensitivity" {
	/* Default: i;ascii-casemap */

	if not address :is ["to", "from"] "nico@nl.example.com" {
		test_fail "address comparator is i;octet by default (1)";
	}

	if not address :is ["to", "from"] "harry@de.example.com" {
		test_fail "address comparator is i;octet by default (2)";
	}

	if not address :is ["to", "from"] "STEPHAN@example.com" {
		test_fail "address comparator is i;octet by default (3)";
	}

	if not address :is :localpart ["to"] "nico" {
		test_fail "address comparator is i;octet by default (4)";
	}

	/* Match case-sensitively */

	if not address :is :comparator "i;octet" ["to"] "Nico@nl.example.com" {
		test_fail "failed to match case-sensitive address (1)";
	}

	if not address :is :comparator "i;octet" ["to"] "harry@DE.EXAMPLE.COM" {
		test_fail "failed to match case-sensitive address (2)";
	}

	if address :is :comparator "i;octet" ["to"] "harry@de.example.com" {
		test_fail "failed to notice case difference in address with i;octet (1)";
	}

	if address :is :comparator "i;octet" ["from"] "STEPHAN@example.com" {
		test_fail "failed to notice case difference in address with i;octet (2)";
	}

	if not address :is :localpart :comparator "i;octet" ["to"] "Nico" {
		test_fail "failed to match case-sensitive localpart";
	}

	if address :is :localpart :comparator "i;octet" ["to"] "nico" {
		test_fail "failed to notice case difference in local_part with i;octet";
	}

	if not address :is :domain :comparator "i;octet" ["to"] "DE.EXAMPLE.COM" {
		test_fail "failed to match case-sensitive localpart";
	}

	if address :is :domain :comparator "i;octet" ["to"] "de.example.com" {
		test_fail "failed to notice case difference in domain with i;octet";
	}
}

/*
 * TEST: Phrase part, comments and group names
 */

/* "The address primitive never acts on the phrase part of an email
 *  address or on comments within that address.  It also never acts on
 *  group names, ...
 * "
 */

test_set "message" text:
From: Stephan Bosch <stephan(the author)@example.com>
To: Nico Thalens <nico@nl.example.com>, Harry Becker <harry@de.example.com>
cc: tukkers: henk@tukkerland.ex, theo@tukkerland.ex, frits@tukkerland.ex;
Subject: Frobnitzm

Test.
.
;

test "Phrase part, comments and group names" {
	if address :contains :all :comparator "i;ascii-casemap"
		["to","from"] ["Bosch", "Thalens", "Becker"] {
		test_fail "matched phrase part";
	}

	if address :contains :all :comparator "i;ascii-casemap" "from" "author" {
		test_fail "matched comment";
	}


	if address :contains :all :comparator "i;ascii-casemap" ["cc"] ["tukkers"] {
		test_fail "matched group name";
	}
}


/*
 * TEST: Group addresses
 */

/* "... although it does act on the addresses within the group
 *  construct.
 * "
 */

test_set "message" text:
From: stephan@friep.frop
To: undisclosed-recipients:;
cc: tukkers: henk@tukkerland.ex, theo@tukkerland.ex, frits@tukkerland.ex;
Subject: Invalid addresses

Test.
.
;

test "Group addresses" {
	if not address :is :domain ["cc"] ["tukkerland.ex"] {
		test_fail "failed to match group address (1)";
	}

	if not address :is :localpart ["cc"] ["henk"] {
		test_fail "failed to match group address (2)";
	}

	if not address :is :localpart ["cc"] ["theo"] {
		test_fail "failed to match group address (3)";
	}

	if not address :is :localpart ["cc"] ["frits"] {
		test_fail "failed to match group address (4)";
	}
}

/*
 * TEST: Address headers
 */

/* "Implementations MUST restrict the address test to headers that
 *  contain addresses, but MUST include at least From, To, Cc, Bcc,
 *  Sender, Resent-From, and Resent-To, and it SHOULD include any other
 *  header that utilizes an "address-list" structured header body.
 * "
 */

test_set "message" text:
From: stephan@friep.frop
To: henk@tukkerland.ex
CC: ivo@boer.ex
Bcc: joop@hooibaal.ex
Sender: s.bosch@friep.frop
Resent-From: ivo@boer.ex
Resent-To: idioot@dombo.ex
Reply-To: aaaa@friep.frop
Resent-Reply-To: bbbb@friep.frop
Resent-Sender: cccc@friep.frop
Resent-Cc: dddd@friep.frop
Resent-Bcc: eeee@friep.frop
For-Approval: ffff@friep.frop
For-Handling: gggg@friep.frop
For-Comment: hhhh@friep.frop
Apparently-To: iiii@friep.frop
Errors-To: jjjj@friep.frop
Delivered-To: kkkk@friep.frop
return-receipt-to: llll@friep.frop
X-Admin: mmmm@friep.frop
Read-Receipt-To: nnnn@friep.frop
X-Confirm-Reading-To: oooo@friep.frop
Return-Receipt-Requested: pppp@friep.frop
Registered-Mail-Reply-Requested-By: qqqq@friep.frop
Mail-Followup-To: rrrr@friep.frop
Mail-Reply-To: ssss@friep.frop
Abuse-Reports-To: tttt@friep.frop
X-Complaints-To: uuuu@friep.frop
X-Report-Abuse-To: vvvv@friep.frop
X-BeenThere: wwww@friep.frop
X-Original-From: xxxx@friep.frop
X-Original-To: yyyy@friep.frop
Subject: Berichtje:

Test.
.
;


test "Address headers" {
	/* Required */

	if not address "from" "stephan@friep.frop" {
		test_fail "from header not recognized";
	}

	if not address "to" "henk@tukkerland.ex" {
		test_fail "to header not recognized";
	}

	if not address "cc" "ivo@boer.ex" {
		test_fail "cc header not recognized";
	}

	if not address "bcc" "joop@hooibaal.ex" {
		test_fail "bcc header not recognized";
	}

	if not address "sender" "s.bosch@friep.frop" {
		test_fail "sender header not recognized";
	}

	if not address "resent-from" "ivo@boer.ex" {
		test_fail "resent-from header not recognized";
	}

	if not address "resent-to" "idioot@dombo.ex" {
		test_fail "resent-to header not recognized";
	}

	/* Additional (RFC 822 / RFC 2822) */

	if not address "reply-to" "aaaa@friep.frop" {
		test_fail "reply-to header not recognized";
	}

	if not address "resent-reply-to" "bbbb@friep.frop" {
		test_fail "resent-reply-to header not recognized";
	}

	if not address "resent-sender" "cccc@friep.frop" {
		test_fail "resent-sender header not recognized";
	}

	if not address "resent-cc" "dddd@friep.frop" {
		test_fail "resent-cc header not recognized";
	}

	if not address "resent-bcc" "eeee@friep.frop" {
		test_fail "resent-bcc header not recognized";
	}

	/* Non-standard (RFC 2076, draft-palme-mailext-headers-08.txt) */

	if not address "for-approval" "ffff@friep.frop" {
		test_fail "for-approval header not recognized";
	}

	if not address "for-handling" "gggg@friep.frop" {
		test_fail "for-handling header not recognized";
	}

	if not address "for-comment" "hhhh@friep.frop" {
		test_fail "for-comment header not recognized";
	}
	if not address "apparently-to" "iiii@friep.frop" {
		test_fail "apparently-to header not recognized";
	}
	
	if not address "errors-to" "jjjj@friep.frop" {
		test_fail "errors-to header not recognized";
	}
	
	if not address "delivered-to" "kkkk@friep.frop" {
		test_fail "delivered-to header not recognized";
	}
	
	if not address "return-receipt-to" "llll@friep.frop" {
		test_fail "return-receipt-to header not recognized";
	}

	if not address "x-admin" "mmmm@friep.frop" {
		test_fail "x-admin header not recognized";
	}
	
	if not address "read-receipt-to" "nnnn@friep.frop" {
		test_fail "read-receipt-to header not recognized";
	}
	
	if not address "x-confirm-reading-to" "oooo@friep.frop" {
		test_fail "x-confirm-reading-to header not recognized";
	}

	if not address "return-receipt-requested" "pppp@friep.frop" {
		test_fail "return-receipt-requested header not recognized";
	}

	if not address "registered-mail-reply-requested-by" "qqqq@friep.frop" {
		test_fail "registered-mail-reply-requested-by header not recognized";
	}
	
	if not address "mail-followup-to" "rrrr@friep.frop" {
		test_fail "mail-followup-to header not recognized";
	}
	
	if not address "mail-reply-to" "ssss@friep.frop" {
		test_fail "mail-reply-to header not recognized";
	}
	
	if not address "abuse-reports-to" "tttt@friep.frop" {
		test_fail "abuse-reports-to header not recognized";
	}

	if not address "x-complaints-to" "uuuu@friep.frop" {
		test_fail "x-complaints-to header not recognized";
	}
	
	if not address "x-report-abuse-to" "vvvv@friep.frop" {
		test_fail "x-report-abuse-to header not recognized";
	}

	/* Undocumented */
	
	if not address "x-beenthere" "wwww@friep.frop" {
		test_fail "x-beenthere header not recognized";
	}

	if not address "x-original-from" "xxxx@friep.frop" {
		test_fail "x-original-from header not recognized";
	}

	if not address "x-original-to" "yyyy@friep.frop" {
		test_fail "x-original-to header not recognized";
	}
}

/* ## RFC 5228, Section 2.7.4. Comparisons against Addresses (page 16) ## */

/*
 * TEST: Invalid addresses
 */

/*
 * "If an address is not syntactically valid, then it will not be matched
 *  by tests specifying ":localpart" or ":domain".
 * "
 */

test_set "message" text:
From: stephan@
To: @example.org
Cc: nonsense
Resent-To:
Bcc: nico@frop.example.com, @example.org
Resent-Cc:<jürgen@example.com>
Subject: Invalid addresses

Test.
.
;

test "Invalid addresses" {
	if address :localpart "from" "stephan" {
		test_fail ":localpart matched invalid address";
	}

#UTF8#	if address :localpart "resent-cc" "jürgen" {
#UTF8#		test_fail ":localpart matched invalid UTF-8 address";
#UTF8#	}

	if address :domain "to" "example.org" {
		test_fail ":domain matched invalid address";
	}

#UTF8#	if address :domain "resent-cc" "example.com" {
#UTF8#		test_fail ":domain matched invalid UTF-8 address";
#UTF8#	}

	if not address :is :all "resent-to" "" {
		test_fail ":all failed to match empty address";
	}

	if not address :is :all "cc" "nonsense" {
		test_fail ":all failed to match invalid address";
	}

#UTF8#	if not address :is :all "resent-cc" "<jürgen@example.com>" {
#UTF8#		test_fail ":all failed to match invalid UTF-8 address";
#UTF8#	}

	if address :is :localpart "bcc" "" {
		test_fail ":localpart matched invalid address";
	}

	if address :is :domain "cc" "example.org" {
		test_fail ":domain matched invalid address";
	}
}

/*
 * TEST: Default address part
 */

/* "If an optional address-part is omitted, the default is ":all".
 * "
 */

test_set "message" text:
From: stephan@example.com
To: nico@nl.example.com, harry@de.example.com
Subject: Frobnitzm

Test.
.
;

test "Default address part" {
	if not address :is :comparator "i;ascii-casemap" "from" "stephan@example.com"
		{
		test_fail "invalid default address part (1)";
	}

	if not address :is :comparator "i;ascii-casemap" "to"
		["harry@de.example.com"] {
		test_fail "invalid default address part (2)";
	}
}

/*
 * TEST: Mime encoding of '@' in display name
 */

test_set "message" text:
From: "Frop <frop@example.org>"
To: =?UTF-8?B?RnJpZXBAZnJvcA0K?=
	<friep@example.com>
Subject: Test

Frop!
.
;


test "Mime encoding of '@' in display name" {
        # Relevant sieve rule:

        if not address :is "To" 
			["friep@example.com"] {
                test_fail "Invalid address extracted";
        }
}

/*
 * TEST: Erroneous mime encoding
 */

test_set "message" text:
From: "William Wallace <william@scotsmen.ex>"
To: "=?UTF-8?B?IkR1bWIgTWFpbGVyIg==?="
	<horde@lists.scotsmen.ex>
Subject: Test

Frop!
.
;


test "Erroneous mime encoding" {
        # Relevant sieve rule:

        if not address :is ["To","CC"] ["horde@lists.scotsmen.ex","archers@lists.scotsmen.ex"] {
                test_fail "Failed to match improperly encoded address headers";
        }
}


