-- -- A simple mail queue -- -- @author eric@openforce.net -- @version $Id: acs-mail-lite-create.sql,v 1.23 2024/09/11 06:15:48 gustafn Exp $ -- create sequence acs_mail_lite_id_seq; CREATE TABLE acs_mail_lite_queue ( message_id integer constraint acs_mail_lite_queue_pk primary key, creation_date text, locking_server text, to_addr text, cc_addr text, bcc_addr text, from_addr text, reply_to text, subject text, body text, package_id integer constraint amlq_package_id_fk references apm_packages, file_ids text, filesystem_files text, delete_filesystem_files_p boolean, mime_type text, object_id integer, no_callback_p boolean, extraheaders text, use_sender_p boolean ); create index acs_mail_lite_queue_package_id_idx on acs_mail_lite_queue(package_id); create table acs_mail_lite_mail_log ( party_id integer constraint amlml_party_id_fk references parties (party_id) on delete cascade constraint acs_mail_lite_log_pk primary key, last_mail_date timestamptz default current_timestamp ); create table acs_mail_lite_bounce ( party_id integer constraint amlb_party_id_fk references parties (party_id) on delete cascade constraint acs_mail_lite_bou_pk primary key, bounce_count integer default 1 ); create table acs_mail_lite_bounce_notif ( party_id integer constraint amlbn_party_id_fk references parties (party_id) on delete cascade constraint acs_mail_lite_bounce_notif_pk primary key, notification_time timestamptz default current_timestamp, notification_count integer default 0 ); -- -- Add Incoming Mail Processing -- create sequence acs_mail_lite_in_id_seq; -- New tables -- table tracking incoming email create table acs_mail_lite_from_external ( aml_email_id integer primary key not null DEFAULT nextval ('acs_mail_lite_id_seq'), -- Priority for processing incoming email in queue. -- Lower number processed first. priority integer, -- using varchar instead of text for indexing -- to and from email are defined according to headers. -- See table acs_mail_lite_ie_headers to_email_addrs varchar(1000), from_email_addrs text, subject text, -- see acs_mail_lite_send_msg_id_map.msg_id msg_id bigint, -- used by prioritization calculations -- For IMAP4 this is size defined by rfc822 size_chars numeric, -- time email received from server in seconds since tcl epoch received_cs bigint, -- Answers question: -- Has all ACS Mail Lite processes finished for this email? -- Processes like parsing email, bounced email, input validation processed_p boolean, -- Answers question: -- Have all callbacks related to this email finished processing? -- Upon release, delete all components of aml_email_id also from -- tables acs_mail_lite_ie_headers, acs_mail_lite_ie_body_parts, and -- acs_mail_lite_ie_files. -- Release essentially means its available to be deleted. release_p boolean ); create index acs_mail_lite_from_external_processed_p_idx on acs_mail_lite_from_external (processed_p); create index acs_mail_lite_from_external_release_p_idx on acs_mail_lite_from_external (release_p); -- Some services are offered between sessions of importing incoming email. -- A unique ID provided by -- acs_mail_lite_email_uid_id_map.uid_ext -- is designed to -- support UIDs for each email that are consistent between import sessions -- from external source, such as specified by IMAP4 rfc3501 -- https://tools.ietf.org/html/rfc3501 -- It is also expected that each mailbox.host, mailbox and user are -- consistent for duration of the service. -- And yet, experience knows that sometimes email servers change -- and UIDs for an email change with it. -- Users switching email servers of an email account using a IMAP4 client -- might hassle with moving email, but -- in the process they generally know what is happening. They don't re-read -- all the email. -- We want to avoid this server re-reading and processing email -- that has already been processed, when the UID of emails change. -- The Questions become: -- What scenarios might we run into? -- Another user resetting flags. -- A server migration or restore with some conflicting UIDs. -- Can we recognize a change in server? -- If so, can we signal ACS Mail Lite to ignore existing email -- in a new environment? -- Also, we should have a manual override to not ignore or ignore -- in case of false positive and false negative triggers. -- Can we recognize if another user accesses the same email account -- and arbitrarily selects some prior messages to unread? -- Yes. The purpose of acs_mail_lite_email_uid_id_map is to act as a log -- of prior processed messages. -- If total new messages is a significant percentage of all messages -- and service has been working for a week or more, -- use statistics to generate a reasonable trigger. -- Weekly produce a revised distribution curve of weekly counts of incoming. -- If percentile is over 200%.. ie twice the weekly maximum.. -- Also, test, for example, if new message count is less than -- prior total, there's more of a chance that they are new messages; -- Maybe check for one or two duplicates. -- If new message count is over the total prior message count, flag a problem. -- rfc3501 2.3.1.1. ..A client can only assume.. at the time -- that it obtains the next unique identifier value.. that -- messages arriving after that time will have a UID greater -- than or equal to that value... -- Can we recognize a change in server? -- rfc3501 does not specify a unique server id -- It specifies a unique id for a mailbox: UIDVALIDITY -- UIDVALIDITY is optional, quite useful. -- Rfc3501 specifies a unique id for each email: UID. -- We can assign each email a more unique reference: -- mailbox.host + mailbox.name + UIDVALIDITY (of mailbox) + UID. -- We are more specific so that we detect more subtle cases of -- server change, where checks by UID and UIDVALIDITY may not. -- For example, when migrating email service and -- and the new system initially restores the UIVALIDITY and message UID, -- but references a different state of each email. The cause -- of such cases are reasonable. For example, restoring -- from backup to a new email host or restoring -- before some batch event changed a bunch of things. So, -- src_ext = mailbox.host + (user?) + mailbox.name + UIDVALIDITY -- Leave user out for now.. -- Priority is to have a robust way to ignore -- prior messages recognized as 'new' messages. create table acs_mail_lite_email_uid_id_map ( -- unqique internal inbound email id -- src_ext_id identifies source, but is redundant -- for identifying a unique email. aml_email_id integer not null, --uisng varchar instead of text for indexing purposes -- Each UID externally defined such as from imap4 server uid_ext varchar(3000) not null, -- Each external source may apply a different uid. -- This is essentially an arbitrary constant frame reference between -- connecting sessions with external server in most scenarios. -- For IMAP4v1 rfc3501 2.3.1.1. item 4 ..combination of -- mailbox.name, UIDVALIDITY, and UID must refer to a single -- immutable message on that server forever. -- default is: -- ExternalSource parameter mailbox.name -- and UIDVALIDITY with dash as delimiter -- where ExternalSource parameter is -- either blank or maybe mailbox.host for example. -- external source reference id -- see acs_mail_lite_email_src_ext_id_map.aml_src_id src_ext_id integer ); create index acs_mail_lite_email_uid_id_map_uid_ext_idx on acs_mail_lite_email_uid_id_map (uid_ext); create index acs_mail_lite_email_uid_id_map_src_ext_id_idx on acs_mail_lite_email_uid_id_map (src_ext_id); create table acs_mail_lite_email_src_ext_id_map ( aml_src_id integer not null, src_ext varchar(1000) not null ); -- Packages that are services, such as ACS Mail Lite, do not have a web UI. -- Scheduled procs cannot read changes in values of package parameters -- or get updates via web UI connections, or changes in tcl via apm. -- Choices are updates via nsv variables and database value updates. -- Choices via database have persistence across server restarts. -- Defaults are set in acs_mail_lite::sched_parameters -- These all are used in context of processing incoming email -- unless stated otherwise. -- Most specific flag takes precedence. -- If an email is flagged high priority by package_id and -- low priority by subject glob. It is assigned low priority. -- Order of specificity. -- medium default package_id party_id subject_id object_id -- party_id can be group_id or user_id -- If fast and low flag the same specificity for an email, low is chosen. create table acs_mail_lite_ui ( -- scan_replies_est_dur_per_cycle_s_override see www/doc/analysis-notes sredpcs_override integer, -- Answers question: Reprocess old email? reprocess_old_p boolean, -- Max number of concurrent threads for high priority processing max_concurrent integer, -- Any incoming email body part over this size is stored in file -- instead of database. max_blob_chars integer, -- Minimum threshold for default medium (standard) priority mpri_min integer, -- Maximum value for default medium (standard) priority mpri_max integer, --space delimited list of package_ids to process at fast/high priority hpri_package_ids text, --space delimited list of package_ids to process at low priority lpri_package_ids text, --space delimited list of party_ids to process at fast/high priority hpri_party_ids text, --space delimited list of party_ids to process at low priority lpri_party_ids text, -- a glob for searching subjects to flag for fast/high priority hpri_subject_glob text, -- a glob for searching subjects to flag for low priority lpri_subject_glob text, --space delimited list of object_ids to process at fast/high priority hpri_object_ids text, --space delimited list of object_ids to process at low priority lpri_object_ids text, --filters to reject input as early as possible in processing inbound --Each filter is a name value in standard tcl list format --where name is a header name reject_on_hit text, reject_on_miss text ); -- This table has similar requirements to acs_mail_lite_ui -- proc acs_mail_lite_imap_conn_* needs to be able to update values -- within scheduled procs without restarting server. -- Port is ignored. Added because it is a common requirement of connections -- that might one day be useful here, too. create table acs_mail_lite_imap_conn ( -- mailbox.host ho text, -- you guessed it pa text, -- port po integer, --timeout ti integer, -- user us text, -- mailbox.name See nsimap documentation for definition na text, -- space separated list of flags for imap related modifications -- ssl means connect via ssl. -- novalidatecert means accept a self-signed certificate fl text ); -- Following tables store parsed incoming email for processing by callbacks -- defined in the rest of OpenACS -- incoming email headers -- There should be a size limit per unit time from each source -- to prevent DDOS attacks and such (at least to the imap system). -- create table acs_mail_lite_ie_headers ( -- incoming email -- only includes headers useful in processing the queue -- Such as -- size -- from aml_email_id integer, -- header name, one header per row -- For all headers together, see acs_mail_lite_ie_parts.c_type=headers -- Special case: h_name = struct means -- h_value contains entire value returned from ns_imap struct -- as a tcl list h_name text, h_value text ); create index acs_mail_lite_ie_headers_aml_email_id_idx on acs_mail_lite_ie_headers (aml_email_id); -- incoming email body parts -- including email file attachments and file content -- A part may be a filename. The filename data model is added -- to the parts table to reduce code complexity. -- An attached or inline file is a kind of part. create table acs_mail_lite_ie_parts ( aml_email_id integer, section_id integer, -- In addition to content_type, there is a special case: -- headers, which contains all headers for email -- content_type = c_type c_type text, -- If type has a filename, this is original filename. filename text, -- If c_type is multipart, content is blank. part_id is branched. content text, -- An alternate filepathname for large blob, or -- A local absolute filepath location c_filepathname text ); create index acs_mail_lite_ie_parts_aml_email_id_idx on acs_mail_lite_ie_parts (aml_email_id); -- incoming email parts, name value pairs of create table acs_mail_lite_ie_part_nv_pairs ( aml_email_id integer, -- Usage is same as acs_mail_lite_ie_parts.section_id section_id integer, -- name value pair p_name text, p_value text ); create index acs_mail_lite_ie_part_nv_pairs_aml_email_id_idx on acs_mail_lite_ie_part_nv_pairs (aml_email_id); create table acs_mail_lite_ie_section_ref_map ( -- 'Section' refers to usage with 'part' reference in 'ns_imap body' -- Email parts can contain multiple parts. -- Each multiple part can contain multiple parts. -- Section_ref is an absolute reference of a part -- including the parts it is contained in, and -- delimited by period. -- It is defined by: -- ns_imap body #s msgno part -- And yet, this reference system holds for any email -- storage, so is adopted for generic use as well. -- Default reference is value of 1. -- A two part message has values 1 and 2. -- Part 2 of a 3 part email (2/3) has reference '2' -- If part 2 is also multiple parts, then -- part 1 of part 2 of email has reference '2.1' and so on. -- Mapping is constant for each case. -- For example, '1.2.2.1' will always point to the same integer. -- So do not alter values as they are likely used by -- multiple emails. section_ref varchar(300), section_id integer ); create index acs_mail_lite_ie_section_ref_map_section_ref_idx on acs_mail_lite_ie_section_ref_map (section_ref); create index acs_mail_lite_ie_section_ref_map_section_id_idx on acs_mail_lite_ie_section_ref_map (section_id); -- -- TODO: this table does not exists for Oracle.... Not needed there? -- create table acs_mail_lite_send_msg_id_map ( -- a randomized number unique to this table -- unique not null msg_id text primary key, package_id integer constraint aml_package_id_fk references apm_packages on delete cascade, party_id integer constraint aml_from_external_party_id_fk references parties (party_id) on delete cascade, object_id integer constraint aml_from_external_obect_id_fk references acs_objects (object_id) on delete cascade, -- Indicate approximate time when this email is created datetime_cs integer, -- other data or parameters to associate with email other text ); -- -- Speed up referential integrity -- CREATE INDEX acs_mail_lite_send_msg_id_map_package_id_idx ON acs_mail_lite_send_msg_id_map(package_id); CREATE INDEX acs_mail_lite_send_msg_id_map_party_id_idx ON acs_mail_lite_send_msg_id_map(party_id); CREATE INDEX acs_mail_lite_send_msg_id_map_object_id_idx ON acs_mail_lite_send_msg_id_map(object_id);