diff --git a/library/runtime/process/notification_email/notification_email-safe.ecf b/library/runtime/process/notification_email/notification_email-safe.ecf
index 01d95e0a..177c5441 100644
--- a/library/runtime/process/notification_email/notification_email-safe.ecf
+++ b/library/runtime/process/notification_email/notification_email-safe.ecf
@@ -1,5 +1,5 @@
-
+
@@ -11,8 +11,20 @@
+
+
+
+
+
-
+
+
+
+
+
+
+
+
diff --git a/library/runtime/process/notification_email/notification_email.e b/library/runtime/process/notification_email/notification_email.e
index 4ca33864..5f835b82 100644
--- a/library/runtime/process/notification_email/notification_email.e
+++ b/library/runtime/process/notification_email/notification_email.e
@@ -2,9 +2,9 @@ note
description : "[
Component representing an email
]"
- author : "$Author$"
- date : "$Date$"
- revision : "$Revision$"
+ author : "$Author: jfiat $"
+ date : "$Date: 2015-06-30 11:07:17 +0200 (mar., 30 juin 2015) $"
+ revision : "$Revision: 97586 $"
class
NOTIFICATION_EMAIL
@@ -14,15 +14,17 @@ create
feature {NONE} -- Initialization
- make (a_from: like from_address; a_to_address: READABLE_STRING_8; a_subject: like subject; a_body: like body)
+ make (a_from: like from_address; a_to_address: READABLE_STRING_8; a_subject: like subject; a_content: like content)
-- Initialize `Current'.
+ require
+ well_formed_from_address: is_valid_address (a_from)
+ well_formed_to_address: a_to_address.has ('@')
do
initialize
from_address := a_from
subject := a_subject
- body := a_body
+ content := a_content
to_addresses.extend (a_to_address)
-
end
initialize
@@ -37,11 +39,36 @@ feature -- Access
from_address: READABLE_STRING_8
+ reply_to_address: detachable READABLE_STRING_8
+
to_addresses: ARRAYED_LIST [READABLE_STRING_8]
+ cc_addresses: detachable ARRAYED_LIST [READABLE_STRING_8]
+
+ bcc_addresses: detachable ARRAYED_LIST [READABLE_STRING_8]
+
subject: READABLE_STRING_8
- body: READABLE_STRING_8
+ content: READABLE_STRING_8
+
+ additional_header_lines: detachable ARRAYED_LIST [READABLE_STRING_8]
+ -- Additional header lines.
+
+ body: like content
+ obsolete
+ "Use `content' [June/2015]"
+ do
+ Result := body
+ end
+
+feature -- Status report
+
+ is_valid: BOOLEAN
+ -- Is current email ready to be sent?
+ do
+ Result := is_valid_address (from_address) and
+ across to_addresses as ic all is_valid_address (ic.item) end
+ end
feature -- Change
@@ -50,13 +77,90 @@ feature -- Change
date := d
end
+ set_subject (s: READABLE_STRING_8)
+ -- Set `subject' to `s'.
+ do
+ subject := s
+ end
+
+ set_content (s: READABLE_STRING_8)
+ -- Set `content' to `s'.
+ do
+ content := s
+ end
+
+ set_from_address (add: READABLE_STRING_8)
+ require
+ well_formed_address: add.has ('@')
+ do
+ from_address := add
+ end
+
+ add_cc_address (add: READABLE_STRING_8)
+ require
+ well_formed_address: add.has ('@')
+ local
+ lst: like cc_addresses
+ do
+ lst := cc_addresses
+ if lst = Void then
+ create lst.make (1)
+ cc_addresses := lst
+ end
+ lst.force (add)
+ end
+
+ add_bcc_address (add: READABLE_STRING_8)
+ require
+ well_formed_address: add.has ('@')
+ local
+ lst: like bcc_addresses
+ do
+ lst := bcc_addresses
+ if lst = Void then
+ create lst.make (1)
+ bcc_addresses := lst
+ end
+ lst.force (add)
+ end
+
+ add_header_line (a_line: READABLE_STRING_8)
+ require
+ well_formed_header_line: a_line.has (':')
+ local
+ lst: like additional_header_lines
+ do
+ lst := additional_header_lines
+ if lst = Void then
+ create lst.make (1)
+ additional_header_lines := lst
+ end
+ lst.force (a_line)
+ end
+
+feature -- Reset
+
+ reset
+ do
+ reset_addresses
+ additional_header_lines := Void
+ end
+
+ reset_addresses
+ -- Reset all addresses.
+ do
+ to_addresses.wipe_out
+ cc_addresses := Void
+ bcc_addresses := Void
+ end
+
feature -- Conversion
message: STRING_8
do
Result := header
Result.append_character ('%N')
- Result.append (body)
+ Result.append (content)
Result.append_character ('%N')
Result.append_character ('%N')
end
@@ -66,13 +170,14 @@ feature -- Conversion
hdate: HTTP_DATE
do
create Result.make (20)
+ if attached reply_to_address as l_reply_to then
+ Result.append ("Reply-To: ")
+ Result.append (l_reply_to)
+ Result.append_character ('%N')
+ end
Result.append ("From: ")
Result.append (from_address)
Result.append_character ('%N')
- Result.append ("Date: ")
- create hdate.make_from_date_time (date)
- hdate.append_to_rfc1123_string (Result)
- Result.append (" GMT%N")
Result.append ("To: ")
across
to_addresses as c
@@ -81,18 +186,67 @@ feature -- Conversion
Result.append_character (';')
end
Result.append_character ('%N')
+ if
+ attached cc_addresses as l_cc and then
+ not l_cc.is_empty
+ then
+ Result.append ("Cc: ")
+ across
+ l_cc as c
+ loop
+ Result.append (c.item)
+ Result.append_character (';')
+ end
+ Result.append_character ('%N')
+ end
+ if
+ attached bcc_addresses as l_bcc and then
+ not l_bcc.is_empty
+ then
+ Result.append ("Bcc: ")
+ across
+ l_bcc as c
+ loop
+ Result.append (c.item)
+ Result.append_character (';')
+ end
+ Result.append_character ('%N')
+ end
Result.append ("Subject: ")
Result.append (subject)
Result.append_character ('%N')
+ Result.append ("Date: ")
+ create hdate.make_from_date_time (date)
+ hdate.append_to_rfc1123_string (Result)
+ Result.append_character ('%N')
+ if attached additional_header_lines as l_lines and then
+ not l_lines.is_empty
+ then
+ across
+ l_lines as ic
+ loop
+ Result.append (ic.item)
+ Result.append_character ('%N')
+ end
+ end
ensure
Result.ends_with ("%N")
end
+feature -- Helpers
+
+ is_valid_address (add: READABLE_STRING_8): BOOLEAN
+ -- Is `add' a valid email address?
+ do
+ -- FIXME: improve email validation
+ Result := add.has ('@')
+ end
+
invariant
-- invariant_clause: True
note
- copyright: "2011-2013, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Eiffel Software and others"
+ copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
source: "[
Eiffel Software
diff --git a/library/runtime/process/notification_email/notification_email.ecf b/library/runtime/process/notification_email/notification_email.ecf
index 80db0f22..353b3b42 100644
--- a/library/runtime/process/notification_email/notification_email.ecf
+++ b/library/runtime/process/notification_email/notification_email.ecf
@@ -11,8 +11,20 @@
+
+
+
+
+
-
+
+
+
+
+
+
+
+
diff --git a/library/runtime/process/notification_email/notification_mailer.e b/library/runtime/process/notification_email/notification_mailer.e
index 01a16b2e..36be1ba2 100644
--- a/library/runtime/process/notification_email/notification_mailer.e
+++ b/library/runtime/process/notification_email/notification_mailer.e
@@ -2,9 +2,9 @@ note
description: "[
Component responsible to send email
]"
- author: "$Author$"
- date: "$Date$"
- revision: "$Revision$"
+ author: "$Author: jfiat $"
+ date: "$Date: 2015-06-30 11:07:17 +0200 (mar., 30 juin 2015) $"
+ revision: "$Revision: 97586 $"
deferred class
NOTIFICATION_MAILER
@@ -45,8 +45,40 @@ feature -- Basic operation
deferred
end
+feature -- Error
+
+ has_error: BOOLEAN
+ -- Previous operation reported error?
+ -- Use `reset_errors', to reset this state.
+ do
+ Result := attached last_errors as lst and then not lst.is_empty
+ end
+
+ reset_errors
+ -- Reset last errors.
+ do
+ last_errors := Void
+ end
+
+ last_errors: detachable ARRAYED_LIST [READABLE_STRING_32]
+ -- Last reported errors since previous `reset_errors' call.
+
+ report_error (a_msg: READABLE_STRING_GENERAL)
+ -- Report error message `a_msg'.
+ local
+ lst: like last_errors
+ do
+ lst := last_errors
+ if lst = Void then
+ create lst.make (1)
+ last_errors := lst
+ end
+ lst.force (a_msg.to_string_32)
+ end
+
+
note
- copyright: "2011-2013, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Eiffel Software and others"
+ copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
source: "[
Eiffel Software
diff --git a/library/runtime/process/notification_email/notification_null_mailer.e b/library/runtime/process/notification_email/notification_null_mailer.e
new file mode 100644
index 00000000..9707200d
--- /dev/null
+++ b/library/runtime/process/notification_email/notification_null_mailer.e
@@ -0,0 +1,34 @@
+note
+ description: "Mailer that does nothing."
+ date: "$Date: 2015-06-30 15:49:56 +0200 (mar., 30 juin 2015) $"
+ revision: "$Revision: 97588 $"
+
+class
+ NOTIFICATION_NULL_MAILER
+
+inherit
+ NOTIFICATION_MAILER
+
+feature -- Status
+
+ is_available: BOOLEAN = True
+ --
+
+feature -- Basic operation
+
+ process_email (a_email: NOTIFICATION_EMAIL)
+ --
+ do
+ end
+
+note
+ copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Eiffel Software and others"
+ license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
+ source: "[
+ Eiffel Software
+ 5949 Hollister Ave., Goleta, CA 93117 USA
+ Telephone 805-685-1006, Fax 805-685-6869
+ Website http://www.eiffel.com
+ Customer support http://support.eiffel.com
+ ]"
+end
diff --git a/library/runtime/process/notification_email/notification_sendmail_mailer.e b/library/runtime/process/notification_email/notification_sendmail_mailer.e
index 2ea09397..e61f06de 100644
--- a/library/runtime/process/notification_email/notification_sendmail_mailer.e
+++ b/library/runtime/process/notification_email/notification_sendmail_mailer.e
@@ -2,9 +2,9 @@ note
description : "[
NOTIFICATION_MAILER using sendmail as mailtool
]"
- author: "$Author$"
- date: "$Date$"
- revision: "$Revision$"
+ author: "$Author: jfiat $"
+ date: "$Date: 2015-06-30 15:49:56 +0200 (mar., 30 juin 2015) $"
+ revision: "$Revision: 97588 $"
class
NOTIFICATION_SENDMAIL_MAILER
@@ -16,23 +16,29 @@ inherit
end
create
- default_create
+ default_create,
+ make_with_location
feature {NONE} -- Initialization
+ make_with_location (a_path: READABLE_STRING_GENERAL)
+ do
+ make (a_path, <<"-t">>)
+ set_stdin_mode (True, "%N.%N%N")
+ end
+
default_create
do
Precursor
- make ("/usr/sbin/sendmail", <<"-t">>)
+ make_with_location ("/usr/sbin/sendmail")
if not is_available then
- make ("/usr/bin/sendmail", <<"-t">>)
+ make_with_location ("/usr/bin/sendmail")
end
- set_stdin_mode (True, "%N.%N%N")
end
note
- copyright: "2011-2013, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Eiffel Software and others"
+ copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
source: "[
Eiffel Software
diff --git a/library/runtime/process/notification_email/notification_storage_mailer.e b/library/runtime/process/notification_email/notification_storage_mailer.e
new file mode 100644
index 00000000..89f28de2
--- /dev/null
+++ b/library/runtime/process/notification_email/notification_storage_mailer.e
@@ -0,0 +1,54 @@
+note
+ description: "Summary description for {NOTIFICATION_STORAGE_MAILER}."
+ author: ""
+ date: "$Date: 2015-06-30 15:49:56 +0200 (mar., 30 juin 2015) $"
+ revision: "$Revision: 97588 $"
+
+class
+ NOTIFICATION_STORAGE_MAILER
+
+inherit
+ NOTIFICATION_MAILER
+
+create
+ make
+
+feature {NONE} -- Initialization
+
+ make (a_storage: NOTIFICATION_EMAIL_STORAGE)
+ do
+ storage := a_storage
+ end
+
+ storage: NOTIFICATION_EMAIL_STORAGE
+
+feature -- Status report
+
+ is_available: BOOLEAN
+ --
+ do
+ Result := storage.is_available
+ end
+
+feature -- Basic operation
+
+ process_email (a_email: NOTIFICATION_EMAIL)
+ --
+ do
+ storage.put (a_email)
+ if storage.has_error then
+ report_error ("Issue storing email.")
+ end
+ end
+
+note
+ copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Eiffel Software and others"
+ license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
+ source: "[
+ Eiffel Software
+ 5949 Hollister Ave., Goleta, CA 93117 USA
+ Telephone 805-685-1006, Fax 805-685-6869
+ Website http://www.eiffel.com
+ Customer support http://support.eiffel.com
+ ]"
+end
diff --git a/library/runtime/process/notification_email/smtp/notification_smtp_mailer.e b/library/runtime/process/notification_email/smtp/notification_smtp_mailer.e
new file mode 100644
index 00000000..21f8b99d
--- /dev/null
+++ b/library/runtime/process/notification_email/smtp/notification_smtp_mailer.e
@@ -0,0 +1,181 @@
+note
+ description: "[
+ Notification mailer based on STMP protocol.
+
+ Note: it is based on EiffelNet {SMTP_PROTOCOL} implementation, and may not be complete.
+ ]"
+ author: "$Author: jfiat $"
+ date: "$Date: 2015-06-30 11:07:17 +0200 (mar., 30 juin 2015) $"
+ revision: "$Revision: 97586 $"
+
+class
+ NOTIFICATION_SMTP_MAILER
+
+inherit
+ NOTIFICATION_MAILER
+
+create
+ make,
+ make_with_user
+
+feature {NONE} -- Initialization
+
+ make (a_smtp_server: READABLE_STRING_8)
+ do
+ make_with_user (a_smtp_server, Void, Void)
+ end
+
+ make_with_user (a_smtp_server: READABLE_STRING_8; a_user: detachable READABLE_STRING_8; a_password: detachable READABLE_STRING_8)
+ -- Initialize `Current'.
+ local
+ i: INTEGER
+ do
+ i := a_smtp_server.index_of (':', 1)
+ if i > 0 then
+ smtp_host := a_smtp_server.substring (1, i - 1)
+ smtp_port := a_smtp_server.substring (i + 1, a_smtp_server.count).to_integer
+ else
+ smtp_host := a_smtp_server
+ smtp_port := 0
+ end
+ username := a_user
+ initialize
+ end
+
+ initialize
+ -- Initialize service.
+ local
+ l_address_factory: INET_ADDRESS_FACTORY
+ do
+ if attached username as u then
+ create smtp_protocol.make (smtp_host, u)
+ else
+ -- Get local host name needed in creation of SMTP_PROTOCOL.
+ create l_address_factory
+ create smtp_protocol.make (smtp_host, l_address_factory.create_localhost.host_name)
+ end
+ if smtp_port > 0 then
+ smtp_protocol.set_default_port (smtp_port)
+ end
+ reset_errors
+ end
+
+ smtp_protocol: SMTP_PROTOCOL
+ -- SMTP protocol.
+
+feature -- Access
+
+ smtp_host: READABLE_STRING_8
+
+ smtp_port: INTEGER
+
+ username: detachable READABLE_STRING_8
+
+feature -- Status
+
+ is_available: BOOLEAN
+ do
+ Result := True
+ end
+
+feature -- Basic operation
+
+ process_email (a_email: NOTIFICATION_EMAIL)
+ -- Process the sending of `a_email'
+ local
+ l_email: EMAIL
+ h: STRING
+ k,v: STRING
+ i: INTEGER
+ hdate: HTTP_DATE
+ do
+ create l_email.make_with_entry (a_email.from_address, addresses_to_header_line_value (a_email.to_addresses))
+ if attached a_email.reply_to_address as l_reply_to then
+ l_email.add_header_entry ({EMAIL_CONSTANTS}.h_reply_to, l_reply_to)
+ end
+
+ if attached a_email.cc_addresses as lst then
+ l_email.add_header_entry ({EMAIL_CONSTANTS}.h_cc, addresses_to_header_line_value (lst))
+ end
+ if attached a_email.bcc_addresses as lst then
+ l_email.add_header_entry ({EMAIL_CONSTANTS}.h_bcc, addresses_to_header_line_value (lst))
+ end
+ l_email.set_message (a_email.content)
+ l_email.add_header_entry ({EMAIL_CONSTANTS}.H_subject, a_email.subject)
+
+ create h.make_empty
+ create hdate.make_from_date_time (a_email.date)
+ hdate.append_to_rfc1123_string (h)
+ l_email.add_header_entry ("Date", h)
+
+ if attached a_email.additional_header_lines as lst then
+ across
+ lst as ic
+ loop
+ h := ic.item
+ i := h.index_of (':', 1)
+ if i > 0 then
+ k := h.head (i - 1)
+ v := h.substring (i + 1, h.count)
+ l_email.add_header_entry (k, v)
+ else
+ check is_header_line: False end
+ end
+ end
+ end
+
+ smtp_send_email (l_email)
+ end
+
+feature {NONE} -- Implementation
+
+ addresses_to_header_line_value (lst: ITERABLE [READABLE_STRING_8]): STRING
+ local
+ l_need_separator: BOOLEAN
+ do
+ create Result.make (10)
+ l_need_separator := False
+ across
+ lst as ic
+ loop
+ if l_need_separator then
+ Result.append_character (',')
+ Result.append_character (' ')
+ else
+ l_need_separator := True
+ end
+ Result.append (ic.item)
+ end
+ end
+
+ smtp_send_email (a_email: EMAIL)
+ -- Send the email represented by `a_email'.
+ local
+ retried: BOOLEAN
+ do
+ if not retried then
+ smtp_protocol.initiate_protocol
+ smtp_protocol.transfer (a_email)
+ smtp_protocol.close_protocol
+ if smtp_protocol.error then
+ report_error ("smtp_protocol reported an error.")
+ end
+ end
+ rescue
+ report_error ("smtp_protocol raised an exception.")
+ retried := True
+ retry
+ end
+
+note
+ copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Eiffel Software and others"
+ license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
+ source: "[
+ Eiffel Software
+ 5949 Hollister Ave., Goleta, CA 93117 USA
+ Telephone 805-685-1006, Fax 805-685-6869
+ Website http://www.eiffel.com
+ Customer support http://support.eiffel.com
+ ]"
+end
+
diff --git a/library/runtime/process/notification_email/storage/notification_email_file_storage.e b/library/runtime/process/notification_email/storage/notification_email_file_storage.e
new file mode 100644
index 00000000..0923b622
--- /dev/null
+++ b/library/runtime/process/notification_email/storage/notification_email_file_storage.e
@@ -0,0 +1,74 @@
+note
+ description: "Store email in specific file (could also be stderr, ...)."
+ date: "$Date: 2015-06-30 15:49:56 +0200 (mar., 30 juin 2015) $"
+ revision: "$Revision: 97588 $"
+
+class
+ NOTIFICATION_EMAIL_FILE_STORAGE
+
+inherit
+ NOTIFICATION_EMAIL_STORAGE
+
+create
+ make
+
+feature {NONE} -- Initialization
+
+ make (a_output_file: FILE)
+ require
+ a_output_file_valid: a_output_file.exists
+ do
+ output := a_output_file
+ end
+
+ output: FILE
+
+feature -- Status report
+
+ is_available: BOOLEAN
+ -- Is associated storage available?
+ do
+ Result := output.exists and output.is_access_writable
+ end
+
+ has_error: BOOLEAN
+ -- Last operation reported an error?
+
+feature -- Storage
+
+ put (a_email: NOTIFICATION_EMAIL)
+ -- Store `a_email'.
+ local
+ retried: BOOLEAN
+ l_close_needed: BOOLEAN
+ do
+ if not retried then
+ has_error := False
+ if not output.is_open_write then
+ output.open_write
+ l_close_needed := True
+ end
+ output.put_string ("%N----%N" + a_email.message)
+ if l_close_needed then
+ output.close
+ else
+ output.flush
+ end
+ end
+ rescue
+ retried := True
+ has_error := True
+ retry
+ end
+
+;note
+ copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Eiffel Software and others"
+ license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
+ source: "[
+ Eiffel Software
+ 5949 Hollister Ave., Goleta, CA 93117 USA
+ Telephone 805-685-1006, Fax 805-685-6869
+ Website http://www.eiffel.com
+ Customer support http://support.eiffel.com
+ ]"
+end
diff --git a/library/runtime/process/notification_email/storage/notification_email_storage.e b/library/runtime/process/notification_email/storage/notification_email_storage.e
new file mode 100644
index 00000000..60e02a06
--- /dev/null
+++ b/library/runtime/process/notification_email/storage/notification_email_storage.e
@@ -0,0 +1,38 @@
+note
+ description: "Abtract interface of email storage."
+ date: "$Date: 2015-06-30 15:49:56 +0200 (mar., 30 juin 2015) $"
+ revision: "$Revision: 97588 $"
+
+deferred class
+ NOTIFICATION_EMAIL_STORAGE
+
+feature -- Status report
+
+ is_available: BOOLEAN
+ -- Is associated storage available?
+ deferred
+ end
+
+ has_error: BOOLEAN
+ -- Last operation reported an error?
+ deferred
+ end
+
+feature -- Storage
+
+ put (a_email: NOTIFICATION_EMAIL)
+ -- Store `a_email'.
+ deferred
+ end
+
+note
+ copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Eiffel Software and others"
+ license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
+ source: "[
+ Eiffel Software
+ 5949 Hollister Ave., Goleta, CA 93117 USA
+ Telephone 805-685-1006, Fax 805-685-6869
+ Website http://www.eiffel.com
+ Customer support http://support.eiffel.com
+ ]"
+end