Property definitions

curl $ CurlMail :: defaultinit
# CURL Mail Request
#
# ~~~
# # Craft mail
# var mail = new CurlMail("sender@example.org",
#                            to=["to@example.org"], cc=["bob@example.org"])
#
# mail.headers_body["Content-Type:"] = """text/html; charset="UTF-8""""
# mail.headers_body["Content-Transfer-Encoding:"] = "quoted-printable"
#
# mail.body = "<h1>Here you can write HTML stuff.</h1>"
# mail.subject = "Hello From My Nit Program"
#
# # Set mail server
# var error = mail.set_outgoing_server("smtps://smtp.example.org:465",
#                                      "user@example.org", "mypassword")
# if error != null then
#     print "Mail Server Error: {error}"
#     exit 0
# end
#
# # Send
# error = mail.execute
# if error != null then
#     print "Transfer Error: {error}"
#     exit 0
# end
# ~~~
class CurlMail
	super CurlRequest
	super NativeCurlCallbacks

	# Address of the sender
	var from: nullable String is writable

	# Main recipients
	var to: nullable Array[String] is writable

	# Subject line
	var subject: nullable String is writable

	# Text content
	var body: nullable String is writable

	# CC recipients
	var cc: nullable Array[String] is writable

	# BCC recipients (hidden from other recipients)
	var bcc: nullable Array[String] is writable

	# HTTP header
	var headers = new HeaderMap is lazy, writable

	# Content header
	var headers_body = new HeaderMap is lazy, writable

	# Protocols supported to send mail to a server
	#
	# Default value at `["smtp", "smtps"]`
	var supported_outgoing_protocol = ["smtp", "smtps"]

	# Helper method to add pair values to mail content while building it (ex: "To:", "address@mail.com")
	private fun add_pair_to_content(str: String, att: String, val: nullable String): String
	do
		if val != null then return "{str}{att}{val}\n"
		return "{str}{att}\n"
	end

	# Helper method to add entire list of pairs to mail content
	private fun add_pairs_to_content(content: String, pairs: HeaderMap): String
	do
		for h_key, h_val in pairs do content = add_pair_to_content(content, h_key, h_val)
		return content
	end

	# Check for host and protocol availability
	private fun is_supported_outgoing_protocol(host: String): CURLCode
	do
		var host_reach = host.split_with("://")
		if host_reach.length > 1 and supported_outgoing_protocol.has(host_reach[0]) then return once new CURLCode.ok
		return once new CURLCode.unsupported_protocol
	end

	# Configure server host and user credentials if needed.
	fun set_outgoing_server(host: String, user: nullable String, pwd: nullable String): nullable CurlResponseFailed
	do
		# Check Curl initialisation
		if not self.curl.is_ok then return answer_failure(0, "Curl instance is not correctly initialized")

		var err

		# Host & Protocol
		err = is_supported_outgoing_protocol(host)
		if not err.is_ok then return answer_failure(err.to_i, err.to_s)

		err = self.curl.native.easy_setopt(new CURLOption.url, host)
		if not err.is_ok then return answer_failure(err.to_i, err.to_s)

		# Credentials
		if not user == null and not pwd == null then
			err = self.curl.native.easy_setopt(new CURLOption.username, user)
			if not err.is_ok then return answer_failure(err.to_i, err.to_s)

			err = self.curl.native.easy_setopt(new CURLOption.password, pwd)
			if not err.is_ok then return answer_failure(err.to_i, err.to_s)
		end

		return null
	end

	# Execute Mail request with settings configured through attribute
	fun execute: nullable CurlResponseFailed
	do
		if not self.curl.is_ok then return answer_failure(0, "Curl instance is not correctly initialized")

		var lines = new Array[String]

		# Headers
		var headers = self.headers
		if not headers.is_empty then
			for k, v in headers do lines.add "{k}{v}"
		end

		# Recipients
		var all_recipients = new Array[String]
		var to = self.to
		if to != null and to.length > 0 then
			lines.add "To:{to.join(",")}"
			all_recipients.append to
		end

		var cc = self.cc
		if cc != null and cc.length > 0 then
			lines.add "Cc:{cc.join(",")}"
			all_recipients.append cc
		end

		var bcc = self.bcc
		if bcc != null and bcc.length > 0 then all_recipients.append bcc

		if all_recipients.is_empty then return answer_failure(0, "There must be at lease one recipient")

		var err = self.curl.native.easy_setopt(new CURLOption.follow_location, 1)
		if not err.is_ok then return answer_failure(err.to_i, err.to_s)

		err = self.curl.native.easy_setopt(new CURLOption.mail_rcpt, all_recipients.to_curlslist)
		if not err.is_ok then return answer_failure(err.to_i, err.to_s)

		# From
		var from = self.from
		if not from == null then
			lines.add "From:{from}"

			err = self.curl.native.easy_setopt(new CURLOption.mail_from, from)
			if not err.is_ok then return answer_failure(err.to_i, err.to_s)
		end

		# Subject
		var subject = self.subject
		if subject == null then subject = "" # Default
		lines.add "Subject: {subject}"

		# Headers body
		var headers_body = self.headers_body
		if not headers_body.is_empty then
			for k, v in headers_body do lines.add "{k}{v}"
		end

		# Body
		var body = self.body
		if body == null then body = "" # Default

		lines.add ""
		lines.add body
		lines.add ""

		err = self.curl.native.register_callback_read(self)
		if not err.is_ok then return answer_failure(err.to_i, err.to_s)

		var content = lines.join("\n")
		err = self.curl.native.register_read_datas_callback(self, content)
		if not err.is_ok then return answer_failure(err.to_i, err.to_s)

		var err_resp = perform
		if err_resp != null then return err_resp

		return null
	end
end
lib/curl/curl.nit:337,1--525,3