Module: dylan-script-internals Copyright: Original Code is Copyright (c) 1995-2004 Functional Objects, Inc. All rights reserved. License: Functional Objects Library Public License Version 1.0 Dual-license: GNU Lesser General Public License Warranty: Distributed WITHOUT WARRANTY OF ANY KIND /// Mailto locators /// Mail accounts define open class () end; define variable *default-account* :: false-or() = #f; define open generic send (to :: , object :: , #key) => (); define open generic send-using (account :: , to :: , object :: , #key) => (); define method send (to :: , object :: , #rest options, #key, #all-keys) => () assert(*default-account*, "Default outgoing mail account details installed."); start-sockets(); apply(send-using, *default-account*, to, object, options); end method; /// SMTP mail define class () constant slot account-host :: , required-init-keyword: host:; constant slot account-port :: = 25, init-keyword: port:; constant slot account-from-address :: , required-init-keyword: from-address:; constant slot account-from-name :: = "", init-keyword: from-name:; end class; // For installing SMTP as the default outgoing mail account define method set-smtp-defaults (#rest options) => () *default-account* := apply(make, , options); end method; define class () constant slot message-recipients = #f, init-keyword: recipients:; constant slot message-header = #f, init-keyword: header:; constant slot message-body, init-keyword: body:; slot message-parsed-header = #f, init-keyword: parsed-header:; end class; define method message-property (message :: , property :: ) => (property :: false-or()) let parsed = message-parsed-header(message); if (~parsed) parsed := message-parsed-header(message) := read-header-from-string(message-header(message)); end; element(parsed, property, default: #f); end method; define method as (class == , string :: ) => (message :: ) let (keys, lines, chars) = read-header-from-string(string); make(, header: copy-sequence(string, end: chars), body: copy-sequence(string, start: chars), parsed-header: keys); end method; define method as-recipients (object :: ) => (seq :: ) list(locator-address(object)) end method; define method as-recipients (object :: ) => (seq :: ) assert(every?(rcurry(instance?, ), object), "All recipients mail locators"); object end method; define method as-recipients-field (recipients :: ) => (field :: ) assert(~empty?(recipients), "Non empty recipients"); reduce1(method (before :: , after :: ) concatenate(before, ", ", after); end, recipients); end method; define method first-line (string :: ) => (substring :: ) let newline-pos = subsequence-position(string, "\n"); if (newline-pos) copy-sequence(string, end: newline-pos) else string end; end method; define method as-message (account :: , string :: , to :: ) => (message :: ) let recipients = as-recipients(to); make(, recipients: recipients, header: format-to-string ("From: %s <%s>\nTo: %s\nSubject: %s\n", account-from-name(account), account-from-address(account), as-recipients-field(recipients), first-line(string)), body: string); end method; define method send-using (account :: , to :: , object :: , #key) => () let message = as-message(account, object, to); with-smtp-stream (stream to account-host(account), port: account-port(account)) write-smtp-from(stream, account-from-address(account)); for (recipient in message-recipients(message)) write-smtp-recipient(stream, recipient); end; write-smtp-data-start(stream); write-new-line-terminated-text(stream, message-header(message)); new-line(stream); write-new-line-terminated-text(stream, message-body(message)); write-smtp-data-end(stream); end; end method; define method write-new-line-terminated-text (stream :: , text :: ) => () write-text(stream, text); if (last(text) ~== '\n') new-line(stream); end; end method; // eof