From f910d6f6367553ce977148a9b0a19c15445b907a Mon Sep 17 00:00:00 2001 From: Philipp korber Date: Tue, 4 Jul 2017 18:46:49 +0200 Subject: Late initial commit --- .gitignore | 5 ++ .travis.yml | 8 +++ Cargo.toml | 26 ++++++++ LICENSE-APACHE | 201 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ LICENSE-MIT | 25 +++++++ README.md | 27 ++++++++ 6 files changed, 292 insertions(+) create mode 100644 .gitignore create mode 100644 .travis.yml create mode 100644 Cargo.toml create mode 100644 LICENSE-APACHE create mode 100644 LICENSE-MIT create mode 100644 README.md diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a10770d --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +/target +Cargo.lock +.idea/ +.vscode/ +*.iml diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..6e09135 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,8 @@ +language: rust +rust: + - stable + - beta + - nightly +matrix: + allow_failures: + - rust: nightly \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..54e0451 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,26 @@ +[package] +authors = ["Philipp korber "] +categories = [] +description = "TODO" +keywords = ["mail", "codec", "mime", "smtp"] +license = "MIT OR Apache-2.0" +name = "mail-codec" +readme = "./README.md" +repository = "https://github.com/dathinab/mail-codec" +version = "0.1.0" + +build = "./build.rs" + +[badges] + +[badges.travis-ci] +branch = "master" +repository = "dathinab/mail-codec" + +[dependencies] +ascii = "0.8.4" +chrono = "0.4.0" +error-chain = "0.10.0" +mime = "0.3.2" +owning_ref = "0.3.3" +quoted_printable = "0.3.0" diff --git a/LICENSE-APACHE b/LICENSE-APACHE new file mode 100644 index 0000000..56434b8 --- /dev/null +++ b/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/LICENSE-MIT b/LICENSE-MIT new file mode 100644 index 0000000..b25ff75 --- /dev/null +++ b/LICENSE-MIT @@ -0,0 +1,25 @@ +Copyright (c) + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..3ef16e6 --- /dev/null +++ b/README.md @@ -0,0 +1,27 @@ + +# mail_codec   [![Build Status](https://travis-ci.org/dathinab/simple_data_tests.svg?branch=master)](https://travis-ci.org/TODO/mail_codec) + +**TODO** + +--- + +TODO + + +Documentation can be [viewed on docs.rs](https://docs.rs/simple_data_tests). + + +## License + +Licensed under either of + + * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) + * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) + +at your option. + +### Contribution + +Unless you explicitly state otherwise, any contribution intentionally submitted +for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any +additional terms or conditions. -- cgit v1.2.3 From 84450ab360520d7354e14f89cef43081ea46d57a Mon Sep 17 00:00:00 2001 From: Philipp Korber Date: Mon, 31 Jul 2017 16:19:35 +0200 Subject: Merged (suashed) in progess branch to master Before ther software enginering process was a bit murky so the history is intentionally seperated by squashing commits Squashed commit of the following: commit c4edcdea7a5208b3475d22ea3f0ff7d608ede2a4 Author: Philipp Korber Date: Mon Jul 31 16:14:16 2017 +0200 fix(mail_composition): now uses embedding extracted from Data commit ce6c156c258f370a8a446ec1ca88a8e7e77bc0a0 Author: Philipp Korber Date: Mon Jul 31 15:15:01 2017 +0200 refactor(warning): fixed warnings by mostly removing unused imports commit b0c6071e35679b3cc1de8b956f6b7bede2a6b2a0 Author: Philipp Korber Date: Mon Jul 31 15:05:47 2017 +0200 fix(component/MessageID): made `from_input` pub commit d4b28974bddb2617854148c46bc497b3be083447 Author: Philipp Korber Date: Mon Jul 31 15:03:13 2017 +0200 chore(encode/Mail): (Encodable-)Mail now impl MailEncodable commit 0732408ed37ae01a82fbc1d4df988d6e09f1879a Author: Philipp Korber Date: Mon Jul 31 13:29:14 2017 +0200 chore(component): DispositionParameter now implements DerefMut FROM NOW IT COMPILES commit 2dd31d525e1abbc640f8b8ad599dd9da845b8871 Author: Philipp Korber Date: Mon Jul 31 13:23:14 2017 +0200 chore(compile): a bunch of small fixes commit 6bb7f0671e3c8217128bb98fa5b5467daa6695e8 Author: Philipp Korber Date: Mon Jul 31 13:00:02 2017 +0200 doc(postponed): component::Diposition::Other, and extension token Currently Disposition is either Inline or Attachment, but the standard allows extensions. Through to support this a general extension token type is needed (should be the same extension token type as for `TransferEncoding` commit 235e9c8499aae345558d462fe557c5decfdd8fbb Author: Philipp Korber Date: Mon Jul 31 12:56:42 2017 +0200 chore(transfer_encoding): simplified transfer encoding commit 2b9396f04096e10db3bb86abcbcc3ef0a39851d7 Author: Philipp Korber Date: Mon Jul 31 12:44:39 2017 +0200 chore(compile): tons of small fixes commit f551eb5d0d62f0bb6347adbeff4152c73652ae1b Author: Philipp Korber Date: Fri Jul 28 11:46:01 2017 +0200 chore(components/old_ones): removed old style components All of them have been moved into their own modules and use the new Input/Item types commit 0c811f124c8069203faed2e7784c681119d3b151 Author: Philipp Korber Date: Fri Jul 28 11:43:55 2017 +0200 chore(components/Unstructured): encoding unstructured improvements It uses now the new style and can write encoded words if nessesary, through currently it collapses FWS into a single space (or CRNL space if needed). commit 048a9996aae3d49c33fd11aa888073bbcb403025 Author: Philipp Korber Date: Tue Jul 25 15:47:57 2017 +0200 fix(components/TransferEncoding): added pub qualifier to method commit 3d832564ea96d2880851cf365c7bc75e8ced6c2a Author: Philipp Korber Date: Tue Jul 25 15:46:00 2017 +0200 chore(components/Mime): for now dont use unsafe The given usage of unsage _should_ be safe, but until taking a closer look at the use case it should not be used commit 0df48d43342808804929727c0999ff95407587a1 Author: Philipp Korber Date: Tue Jul 25 15:44:57 2017 +0200 doc(components): removed outdated doc commit b74d515a6f203c8d77213be9f02c512b6dc552e1 Author: Philipp Korber Date: Tue Jul 25 15:43:45 2017 +0200 chore(components/MessageID): now uses the Input type and Vec1 commit 263b72e23f028b4aebc216d7e343201b40925bac Author: Philipp Korber Date: Tue Jul 25 14:50:00 2017 +0200 chore(components/Path): now uses the email component commit c81ebd5dd5f4328a6147a4a3a726b5caf146170c Author: Philipp Korber Date: Tue Jul 25 14:48:54 2017 +0200 chore(components/Word+ReceivedToken): now uses the word component For this the word component can now be used in a context, where encoded-words are not allowed commit 1ebad1536dfa4671d11cfe1ea5f847fb12ca6a76 Author: Philipp Korber Date: Tue Jul 25 14:17:49 2017 +0200 refactor(Buffer): renamed to FileBuffer it is file specific commit 536b33fa8b2b70ba2163ace00c897dbd55b8a0b0 Author: Philipp Korber Date: Tue Jul 25 14:14:01 2017 +0200 chore(component/text): is combined with Unstructured (Text in RFC 822, Unstructured in RFC 5322 ) commit ad5e4b9403217b60188647ddbdecd922d0adc568 Author: Philipp Korber Date: Tue Jul 25 14:00:01 2017 +0200 chore(mailbox): old style address => new style + rename to Mailbox commit 3cf74ef16c5f047a8272d3e7c0210e9600ec6b92 Author: Philipp Korber Date: Tue Jul 25 11:40:04 2017 +0200 chore(phrase_list): does now use the new Phrase implementation commit 4397f1f2386f1d47b7f2803409371253c776cc83 Author: Philipp Korber Date: Tue Jul 25 11:35:14 2017 +0200 style(codec): negletable whitespace changes commit cf40424426b1f6586c8fe50ce40153a650c4c5ba Author: Philipp Korber Date: Tue Jul 25 11:32:50 2017 +0200 refactor(codec): made mail encoder a trait commit 9bacf2defff09c466a1d1f9b5d70997b2dba04ab Author: Philipp Korber Date: Tue Jul 25 10:59:54 2017 +0200 style(codec): removed out-commented code wrt. 8bit support (now MailType is used, which is more simple and stright forward) commit 07189b305e01c76e3e58561caead675e891df2a2 Author: Philipp Korber Date: Mon Jul 24 21:15:54 2017 +0200 chore(new_components): started moving old component impl to new ones The new components have a better usable interface for mail generation and do no longer have some of the overhead which came from the fact that part of the inner data model was based on idea, from a libary focused on parsing The new Input/Item differenciation allows the usage of String for e.g. creating the display name part in a Mailbox, and is also future prove to support non-utf8 encoded words commit 1f71221dbb33bd4ce7e5a5870e622351c8ae034f Author: Philipp Korber Date: Mon Jul 24 15:57:16 2017 +0200 chore(vec1): replaced Vec usage in templates with Vec1 commit bb549d0968673637ab770321a57eff59a7ea83d7 Author: Philipp Korber Date: Mon Jul 24 15:53:56 2017 +0200 chore(vec1): implemented vec1 commit 515497c5b716ffae127b45aaaf6de2726d0c0e21 Author: Philipp Korber Date: Mon Jul 24 15:23:14 2017 +0200 fix(lib): changed name of util_types to types Sadly the refactoring feature just works "half" the way, so I overlooked this one commit 4b93680958f765a27478d5fac433854bbc1ce563 Author: Philipp Korber Date: Mon Jul 24 15:18:27 2017 +0200 doc(types): add a todo for the needed Vec1/Vec1Plus commit 5a8b8ab9602216a90183cfecbf14de4828152656 Author: Philipp Korber Date: Mon Jul 24 15:17:18 2017 +0200 refactor(util_types): renamed util_types to types commit b4016977bba5d5f89d838b8ec73661ad6f9d4327 Author: Philipp Korber Date: Mon Jul 24 15:16:17 2017 +0200 refactor(types): renamed types to components commit 9a0ac6b65b3ad211ab925c6491dd651cdca9b4e3 Author: Philipp Korber Date: Mon Jul 24 15:13:58 2017 +0200 refactor(utils): moved core type from utils tp util_types Note: util_types needs to be renamed at some point commit eca286eb4d226768c8e9d12e689606af73a65474 Author: Philipp Korber Date: Mon Jul 24 14:57:06 2017 +0200 doc(notes): added more future todo's and notes about ContentDisposition commit bb0dda70782f0ddf67597a87a90cb792af39ea0c Author: Philipp Korber Date: Mon Jul 24 14:56:27 2017 +0200 chore(component/disposition): implemented the Disposition component which is used by the ContentDisposition header Note: support for non token file names is not yet implemnted commit de34057e67ac120d1cd10d2b2d0bcdcbad4eca8f Author: Philipp Korber Date: Mon Jul 24 12:50:34 2017 +0200 chore(headers/spec): added Content-Disposition header commit 89323c1abafd8da7a9b009d17ea0d7d40c63f09a Author: Philipp Korber Date: Mon Jul 24 12:49:29 2017 +0200 doc(notes): added notes about future todos (postponded) commit 38b8ee64e5084f3b9be3e3adde7a5dc78be77ce3 Author: Philipp Korber Date: Mon Jul 24 12:48:33 2017 +0200 chore(mail+composition): improved implementation & interface commit 82d6d0fadb0e6f44d5ae1fc4a3fb721f51d25d71 Author: Philipp Korber Date: Fri Jul 21 22:53:12 2017 +0200 chore(raw_mail): use Resource in Mail (and mail::Builder) commit c731dc21b559f18ccc0ee9d53c0c99a18d0ea1a5 Author: Philipp Korber Date: Fri Jul 21 20:24:13 2017 +0200 chore(buffer): lifted transfer encoding into type system Now there is a TransferEncodedBuffer, which is a buffer containing transfer encoded data + information about which transfer encoding was used commit a39662ff1481fe81dae069c58e6b4d5081a5008b Author: Philipp Korber Date: Fri Jul 21 16:53:31 2017 +0200 chore(raw_mail/body): Buffer now knows if it is transfer encoded With this the body makes sure it will only resolve to transfer encoded Buffers/Data through the encoding might be 7Bit (i.e. "unencoded", but limited in what kind of bytes can appear) commit 49463319b729c2a9e960df37d59ffa63fd46761e Author: Philipp Korber Date: Fri Jul 21 14:53:16 2017 +0200 chore(raw_mail): continued mail implementation, added futures Note: is now out of sync with mail_composition Now uses futures to resolve the body, a mail can be turned into a future resolving back to the mail once all bodies are resolved and other small changes commit 157d0863d857be08f8db1ef50227f6ee6b01016b Author: Philipp Korber Date: Fri Jul 21 12:39:03 2017 +0200 fix(transfer_encoding): use Buffer instead of ByteStream commit 10b54cd7af1970d067784c9b1015e69c58df5ac2 Author: Philipp Korber Date: Fri Jul 21 12:36:29 2017 +0200 chore(transfer_encoding): added basic transfer encoding infrastructure Added basic transfer encoding infrastructure (core encoding, support for looking them up, support for registering extensions) commit 38980c14eb22f247b2e7b789dfa5caed0a166f00 Author: Philipp Korber Date: Fri Jul 21 12:31:54 2017 +0200 chore(utils): added Buffer struct the Buffer struct is for now only a wrapper around Vec but will contain informations about character sets once support for other character sets in input is added using the `encodings` library commit f92f88aeafc83acdd5ddb5db9e88e243cf1f7886 Author: Philipp Korber Date: Fri Jul 21 12:29:49 2017 +0200 doc(notes): added limitations of dependencies base64, quoted_printable have some limiations, which now are noted in notes/notes.md commit fae54036391798f832f000fdeac33f79b7507a1e Author: Philipp Korber Date: Fri Jul 21 12:28:53 2017 +0200 chore(header): improved Header::name() It now returns a struct either containing a `&'static AsciiStr` or a &'a AsciiStr` which allows us to efficiently use `Cor<'static, AsciiStr>` for keys of maps of Headers, as in most* castes the name is `&'static AsciiStr` *nearly all, only Header::Other does not has a `&'static AsciiStr` commit 98339ef489f89245874c3b303b59e77151cffb2d Author: Philipp Korber Date: Fri Jul 21 12:20:11 2017 +0200 chore(dependencies): added base64 commit 9bfdf8aeae9450630373369594a9bc5695b26207 Author: Philipp korber Date: Tue Jul 18 18:00:22 2017 +0200 chore(dependencies): added lazy_static commit 7d457dabcf17c02c5c59cd797be13428466346a9 Author: Philipp korber Date: Tue Jul 18 16:31:55 2017 +0200 doc(notes): extended notes Added RFC to multipart/related Added more info about MIME (trees) Added postponded section commit 2b54bdb8b73e0b36654f64a129d9516b3ae1b77f Author: Philipp korber Date: Mon Jul 17 16:32:08 2017 +0200 chore(mail_composition): worked on interface/implemenation added a scetch implementation of the public mail_composition interface to make sure it's usable commit f01c7dc7e121b0dd11e38de85f8fc44604028e5c Author: Philipp korber Date: Mon Jul 17 16:30:21 2017 +0200 fix(mail_composition.resource): make is_ascii pub commit dcd7b806b80e7e845a6019c5e84f00bdff1ae30a Author: Philipp korber Date: Mon Jul 17 16:29:02 2017 +0200 fix(raw_mail): make source Stream Send use BoxStream, makes the boxed Stream Send, also is more readable commit 242ea901e3e7e343c7ea1f364b56f6e90e053e5f Author: Philipp korber Date: Mon Jul 17 16:25:52 2017 +0200 chore(lib): add serde_derive commit e49d2398c4bbe4abae607b1f26e976b42e5ac3a8 Author: Philipp korber Date: Mon Jul 17 15:11:56 2017 +0200 chore(mail_composition): initial interface scratched out initial interface for mail_compositon including raw_mail commit 41c19e105145e9059fc83386e33815987e508176 Author: Philipp korber Date: Mon Jul 17 15:10:05 2017 +0200 Added futures and serde dependencies commit cd7f9778d9e8e24c46dcbd7da02b77c7d2fe2f59 Author: Philipp korber Date: Mon Jul 17 15:07:01 2017 +0200 chore(encoder+decoder): implemented initial is_* methods is_qtext, is_atext, is_special, is_ctext, is_vchar NOTE: is now out of sync with initial parser nom grammars commit 14467c8a4db063c99bac4cdfe0dd7fa33c8063b7 Author: Philipp korber Date: Mon Jul 17 15:04:43 2017 +0200 chore(error): added mail body related errors commit fbb26ffc435e67717385b9feab1e44f80e1f8b3b Author: Philipp korber Date: Mon Jul 17 15:03:18 2017 +0200 doc(types/unstructured): added fixme to note that some encoded word case is missing commit b261ca83f1f5576ef3683505d7efdef57c898808 Author: Philipp korber Date: Mon Jul 17 15:02:08 2017 +0200 style(encoder): added missing whitespace commit 491bd1c62a0d3475239fae104a6cb603824dbb03 Author: Philipp korber Date: Mon Jul 17 15:00:32 2017 +0200 fix(decoder): replaced a Err case with `unimplemented!` it should have been `unimplemented!` from the beginning commit 415bc0c3b320c81575af230bf89ea0b4553991e4 Author: Philipp korber Date: Mon Jul 17 14:59:30 2017 +0200 doc(header.spec): changed comment for content description being "Text" it probably should be unstructured commit 4731b964ea0c7cfc9ee56811c763986d8b945666 Author: Philipp korber Date: Mon Jul 17 14:58:19 2017 +0200 doc(codec): added todo for with_context functions commit 079ec517bb598036b2f2424b31cfc7dd4e1812e2 Author: Philipp korber Date: Mon Jul 17 14:56:26 2017 +0200 doc(notes): More information about encoded words commit 4ec4d679bec896e21e5d43084bd51d5f7eee0260 Author: Philipp korber Date: Tue Jul 11 14:57:49 2017 +0200 doc(notes): Added information about 8BITMIME to the notes commit 421c6a470b9e79631e1dda8917e942e7fe67d5d8 Author: Philipp korber Date: Tue Jul 11 14:46:02 2017 +0200 chore(encoder): use Vec instead of String internal A MIME body can contain non-utf8, non-ascii bytes, the internals of the encoder where changed to respect this commit 8c4a0eb933883526162984629feda174bda458d5 Author: Philipp korber Date: Tue Jul 11 13:58:57 2017 +0200 refactor(naming): replaced utf8 with bits8 in names Most places which used "utf8" do just care wether or not they get 7Bit data, there for this functions where renamed commit d27e233ff4d285dc04af27a031aca5b8ed24e28a Author: Philipp korber Date: Tue Jul 11 13:52:54 2017 +0200 refactor(decoder): moved utils as they are also needed by encoder commit 2c2d483fddee9dc1066100d233c0d1486d0b201b Author: Philipp korber Date: Tue Jul 11 13:51:30 2017 +0200 refactor(codec): removed smtp from names Many names contained smtp but had actually nothing to do with smtp (just mime) commit e3cda8614942126b4d8e0267d49c2972abe04801 Author: Philipp korber Date: Tue Jul 11 13:01:42 2017 +0200 chore(parser): nom parsing rules now compile commit e2059b5fdcfbea8f58883764c81ef51716f5510d Author: Philipp korber Date: Tue Jul 11 13:00:42 2017 +0200 refactor(macro/sep_for): use sep_for! when it makes sense Some places had exact the code sep_for! generates, now they use `sep_for!` commit 2bcf092b06c04ef45025a4aed4e5852af6d04425 Author: Philipp korber Date: Mon Jul 10 18:44:50 2017 +0200 chore: sketched out parser implementations commit d9d77629c7d2999adc647f7ddb452005407fa279 Author: Philipp korber Date: Mon Jul 10 18:44:16 2017 +0200 chore: small improvements commit 292427124b0a87615bcb921cd4c73cdfe1fa972b Author: Philipp korber Date: Thu Jul 6 15:53:57 2017 +0200 Implemented most header types, needs fixing of tests and also more of them as well as some other parts commit 49ae6bb5f684a1f4b8bd31b2a4e3ccabbe1b4874 Author: Philipp korber Date: Tue Jul 4 18:51:43 2017 +0200 In process, do not expect it to compile its currently generating encoder cases for types which are not yet implemented --- Cargo.toml | 10 +- README.md | 4 +- build.rs | 133 ++++++++++++ notes/notes.md | 319 +++++++++++++++++++++++++++++ src/char_validators.rs | 299 +++++++++++++++++++++++++++ src/codec/mod.rs | 255 +++++++++++++++++++++++ src/codec/transfer_encoding.rs | 150 ++++++++++++++ src/codec/utf8_to_ascii.rs | 26 +++ src/components/cfws.rs | 44 ++++ src/components/date_time.rs | 15 ++ src/components/disposition.rs | 149 ++++++++++++++ src/components/email.rs | 146 +++++++++++++ src/components/header_name.rs | 46 +++++ src/components/mailbox.rs | 210 +++++++++++++++++++ src/components/mailbox_list.rs | 90 ++++++++ src/components/message_id.rs | 97 +++++++++ src/components/mime.rs | 41 ++++ src/components/mod.rs | 56 +++++ src/components/path.rs | 26 +++ src/components/phrase.rs | 117 +++++++++++ src/components/phrase_list.rs | 27 +++ src/components/received_token.rs | 58 ++++++ src/components/transfer_encoding.rs | 64 ++++++ src/components/unstructured.rs | 144 +++++++++++++ src/components/utils/item.rs | 154 ++++++++++++++ src/components/utils/mod.rs | 5 + src/components/utils/validators.rs | 0 src/components/word.rs | 120 +++++++++++ src/error.rs | 91 +++++++++ src/headers/headers.gen.spec | 78 +++++++ src/headers/mod.rs | 77 +++++++ src/lib.rs | 37 ++++ src/macros.rs | 45 ++++ src/mail/body.rs | 143 +++++++++++++ src/mail/builder.rs | 261 ++++++++++++++++++++++++ src/mail/encode.rs | 0 src/mail/mime.rs | 70 +++++++ src/mail/mod.rs | 352 ++++++++++++++++++++++++++++++++ src/mail/resource.rs | 43 ++++ src/mail/utils.rs | 0 src/mail_composition/context.rs | 27 +++ src/mail_composition/data.rs | 138 +++++++++++++ src/mail_composition/mod.rs | 395 ++++++++++++++++++++++++++++++++++++ src/mail_composition/resource.rs | 13 ++ src/mail_composition/templates.rs | 25 +++ src/types/buffer.rs | 79 ++++++++ src/types/date_time.rs | 13 ++ src/types/file_meta.rs | 18 ++ src/types/mod.rs | 11 + src/types/vec1.rs | 158 +++++++++++++++ src/utils.rs | 48 +++++ 51 files changed, 4923 insertions(+), 4 deletions(-) create mode 100644 build.rs create mode 100644 notes/notes.md create mode 100644 src/char_validators.rs create mode 100644 src/codec/mod.rs create mode 100644 src/codec/transfer_encoding.rs create mode 100644 src/codec/utf8_to_ascii.rs create mode 100644 src/components/cfws.rs create mode 100644 src/components/date_time.rs create mode 100644 src/components/disposition.rs create mode 100644 src/components/email.rs create mode 100644 src/components/header_name.rs create mode 100644 src/components/mailbox.rs create mode 100644 src/components/mailbox_list.rs create mode 100644 src/components/message_id.rs create mode 100644 src/components/mime.rs create mode 100644 src/components/mod.rs create mode 100644 src/components/path.rs create mode 100644 src/components/phrase.rs create mode 100644 src/components/phrase_list.rs create mode 100644 src/components/received_token.rs create mode 100644 src/components/transfer_encoding.rs create mode 100644 src/components/unstructured.rs create mode 100644 src/components/utils/item.rs create mode 100644 src/components/utils/mod.rs create mode 100644 src/components/utils/validators.rs create mode 100644 src/components/word.rs create mode 100644 src/error.rs create mode 100644 src/headers/headers.gen.spec create mode 100644 src/headers/mod.rs create mode 100644 src/lib.rs create mode 100644 src/macros.rs create mode 100644 src/mail/body.rs create mode 100644 src/mail/builder.rs create mode 100644 src/mail/encode.rs create mode 100644 src/mail/mime.rs create mode 100644 src/mail/mod.rs create mode 100644 src/mail/resource.rs create mode 100644 src/mail/utils.rs create mode 100644 src/mail_composition/context.rs create mode 100644 src/mail_composition/data.rs create mode 100644 src/mail_composition/mod.rs create mode 100644 src/mail_composition/resource.rs create mode 100644 src/mail_composition/templates.rs create mode 100644 src/types/buffer.rs create mode 100644 src/types/date_time.rs create mode 100644 src/types/file_meta.rs create mode 100644 src/types/mod.rs create mode 100644 src/types/vec1.rs create mode 100644 src/utils.rs diff --git a/Cargo.toml b/Cargo.toml index 54e0451..ebac713 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,6 @@ [package] authors = ["Philipp korber "] +build = "./build.rs" categories = [] description = "TODO" keywords = ["mail", "codec", "mime", "smtp"] @@ -9,8 +10,6 @@ readme = "./README.md" repository = "https://github.com/dathinab/mail-codec" version = "0.1.0" -build = "./build.rs" - [badges] [badges.travis-ci] @@ -19,8 +18,15 @@ repository = "dathinab/mail-codec" [dependencies] ascii = "0.8.4" +base64 = "0.6.0" chrono = "0.4.0" error-chain = "0.10.0" +futures = "0.1.14" +lazy_static = "0.2.8" mime = "0.3.2" +nom = "3.1.0" owning_ref = "0.3.3" quoted_printable = "0.3.0" +rand = "0.3.15" +serde = "1.0.10" +serde_derive = "1.0.10" diff --git a/README.md b/README.md index 3ef16e6..7291a4e 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ -# mail_codec   [![Build Status](https://travis-ci.org/dathinab/simple_data_tests.svg?branch=master)](https://travis-ci.org/TODO/mail_codec) +# mail_codec   [![Build Status](https://travis-ci.org/dathinab/mail_codec.svg?branch=master)](https://travis-ci.org/dathinab/mail_codec) **TODO** @@ -8,7 +8,7 @@ TODO -Documentation can be [viewed on docs.rs](https://docs.rs/simple_data_tests). +Documentation can be [viewed on docs.rs](https://docs.rs/mail-codec). ## License diff --git a/build.rs b/build.rs new file mode 100644 index 0000000..d801beb --- /dev/null +++ b/build.rs @@ -0,0 +1,133 @@ +use std::path::{ Path, PathBuf }; +use std::ascii::AsciiExt; +use std::fs::File; +use std::io::{ Write, BufWriter, BufRead, BufReader, Error as IoError }; +use std::env; +use std::env::VarError; + +fn main() { + generate_html_header( "./src/headers/headers.gen.spec" ).unwrap(); +} + + +fn generate_html_header>( spec: P ) -> Result<(), Error> { + let out = PathBuf::from( env::var( "OUT_DIR" )? ); + let file = File::open( spec )?; + let mut enum_output = BufWriter::new( File::create( out.join( "header_enum.rs.partial" ) )? ); + let mut mail_encodable_impl = BufWriter::new( File::create( out.join( "mail_encodable_impl.rs.partial" ) )? ); + let mut decode_match_output = BufWriter::new( File::create( out.join( "decoder_match_cases.rs.partial" ) )? ); + let mut names_output = BufWriter::new( File::create( out.join( "header_enum_names.rs.partial" ) )? ); + + writeln!( &mut enum_output, "pub enum Header {{" )?; + writeln!( &mut mail_encodable_impl, concat!( + "impl MailEncodable for Header {{\n", + " fn encode( &self, encoder: &mut E ) -> Result<()> where E: MailEncoder {{\n", + " use self::Header::*;\n", + " match *self {{\n" + ))?; + + writeln!( &mut decode_match_output, "{{ fn fn_impl(header_name: &str, data: &str) -> Result
{{ match header_name {{" )?; + writeln!( &mut names_output, + "{{ fn fn_impl<'a>(header: &'a Header) -> HeaderNameRef<'a> {{ match *header {{" )?; + + let mut next_is_header = true; + for line in BufReader::new( file ).lines() { + let line = line?; + let line = line.trim(); + println!( "LINE: {}", &line ); + if line.starts_with( "--" ) || line.len() == 0 { + continue; + } + let mut parts = line.splitn( 4, "|" ).skip( 1 ).take( 2 ); + let name = parts.next().unwrap().trim(); + let rust_type = parts.next().unwrap().trim(); + + if name.len() == 0 && rust_type.len() == 0 { + continue + } else if name.len() == 0 { + panic!( "name missing, but rust type given" ); + } else if rust_type.len() == 0 { + panic!( "rust type missing, but name given" ); + } + + if next_is_header { + next_is_header = false; + assert_eq!( "Name", name ); + assert_eq!( "Rust-Type", rust_type ); + continue; + } + + assert!( is_valid_header_name( name ) ); + + let enum_name = name.replace( "-", "" ); + + writeln!( &mut enum_output, + "\t{}( {} ),", + enum_name, rust_type )?; + + writeln!( &mut names_output, + "\t{}( .. ) => unsafe {{ HeaderNameRef::Static( AsciiStr::from_ascii_unchecked( {:?} ) ) }},", + enum_name, name )?; + + writeln!( &mut mail_encodable_impl, + concat!( " {}( ref field ) => encode_header_helper(", + " unsafe {{ AsciiStr::from_ascii_unchecked( {:?} ) }},", + " field,", + " encoder)," ), + enum_name, name )?; + + writeln!( &mut decode_match_output, + r"\t{:?} => Self::{}( {}::decode( data )? ),", + name, enum_name, rust_type )?; + + } + + writeln!( &mut enum_output, + "\tOther( HeaderName, Unstructured )" )?; + writeln!( &mut enum_output, "}}" )?; + + writeln!( &mut names_output, + "Other( ref name, .. ) => HeaderNameRef::Other( &*name )" )?; + writeln!( &mut names_output, "}} }} fn_impl }}")?; + + writeln!( &mut mail_encodable_impl, + "\tOther( ref name, ref value ) => encode_header_helper( &*name, value, encoder )")?; + writeln!( &mut mail_encodable_impl, "}} }} }}")?; + + writeln!( &mut decode_match_output, + r"\tname => Self::Other( HeaderName::new( name )?, Unstructured::decode( data )? )" )?; + writeln!( &mut decode_match_output, "}} }} fn_impl }}")?; + + Ok( () ) +} + + +#[derive(Debug)] +enum Error { + IoError(IoError), + VarError(VarError) +} + +impl From for Error { + fn from( err: IoError ) -> Error { + Error::IoError( err ) + } +} + +impl From for Error { + fn from( err: VarError ) -> Error { + Error::VarError( err ) + } +} + +fn is_valid_header_name( name: &str ) -> bool { + name.as_bytes().iter().all( |b| { + match *b { + b'a'...b'z' | + b'A'...b'Z' | + b'0'...b'9' | + b'-' => true, + _ => false + } + }) +} diff --git a/notes/notes.md b/notes/notes.md new file mode 100644 index 0000000..1da3287 --- /dev/null +++ b/notes/notes.md @@ -0,0 +1,319 @@ + +# Outer Most interface + +something like a Mailer which might implement tokio_servie::Service (if +so multiple parameters are wrapped into a tupple) + +mailer contains information like `from` + +`mailer.send_mails( recipients_data, mail_gen )` + +where recipients_data is a iterable mapping from address to recipient specific data, +e.g. `Vec<(Address, Data)>` + +and mail_gen is something like `trait MailGen { fn gen_mail( from, to, data, bits8support ) -> MailBody; }` + +`MailBody` is not `tokio_smtp::MailBody` but has to implement nessesray contraints, +(e.g. implemnting `toki_smtp::IntoMailBody` not that for the beginning this will be +hard encoded but later one a generic variation allowing `smtp` to be switched out +by something else is also possible`) + +MailGen implementations are not done by hand but implemented ontop of something +like a template spec e.g. `struct TemplateSpec { id_template: TemplateId, additional_appendixes: Vec }` + +Where `TemplateId` can is e.g. `reset_link` leading to the creation of a `html` with alternate `plain` +mail iff there is a `reset_link.html` and a `reset_link.plain` template. A `reset_link.html.data` +folder could be used to define inline (mime related) appendixes like embedded images, +but we might want to have a way to define such embeddigns through the data ( +E.g. by mapping `Data => TemplateEnginData` and replacing `EmbeddedFile` variations +by a new related id and adding the `EmbeddedFile(data)` data to the list of embeddings) + + + +# List of parts possible non-ascii and not ascii encodable + +- local-part (address/addr-spec/local-part) + +# Limitations + +Line length limit: + +SHOULD be no more than 78 chars (excluding CRLF!) +MUST NOT be more than 998 chars (excluding CRLF) + +# Orphan `\n`,`\r` + +MUST NOT occur in header (except for folding) +MUST NOT occur in body (except for newline) + +## Header specific limitations + +- encoded word max length of 75 chars +- spaces around encoed words are ignored?? + + +# Email Address part (a@b.e) + +- there is a `domain-literal` version which does use somthing like `[some_thing]`, + we can use puny code for converting domains into ascii but probably can't use + this with `domain-literal`'s + +- `local-part` is `dot-atom` which has leading and trailing `[CFWS]` so comments are alowed + +- MessageId uses a email address like syntax but without supporting spaces/comments + + +# MIME + +fields containing mime types can have parameters with a `; key=value` style +this is mainly used for `multipart/mixed; boundary=blablabla` and similar. + +You have to make sure the boundary does not appear in any of the "sub-bodies", +this is kinda easy for bodies with e.g. content transfer encoding Base64, +but can be tricky in combination with some other content as normal text +can totally contain the boundary. To prevent this: + +- use long boundary strings +- encode the body with base64 even if it's "just" ascii + - OR check the content and encode parts of it if necessary + +you can have multipart in multipart creating a tree, +make sure you don't mix up the boundaries + + +A body part does not have to have any headers, assume default values if +there is no header, bodies which have no header _have to start with a +blank line_ separating 0 headers from the body. + +Header fields of bodies which do not start with `Content-` _are ignored_! + +Contend types: + +- `mixed`, list of sub-bodies with mixed mime types, might be displayed inline or as appendix + - use >>`Content-Disposition` (RFC 2183)<< to controll this, even through it's not standarized yet (or is it by now?) + - default body mime type is `text/plain` +- `digest` for combining muliple messages of content type `message/rfc822` + - e.g. `(multipar/mixed ("table of content") (multipart/digest "message1", "message2"))` + - `message` (mainly `message/rfc822`) contains _another_ email, e.g. for digest + - wait is there a `multipart/message`?? proably not! +- `alternative` multiple alternative versions of the "same" information + - e.g. `(multipart/alternative (text/plain ...) (text/html ...))` + - _place preferred form last!_ (i.e. increasing order of preference) + - interesting usage with `application/X-FixedRecord`+`application/octet-stream` +- `related` (RFC 2387) all bodies are part of one howl, making no (less) sense if placed alone + - the first part is normally the entry point, but this can be chaged through parameters + - (only relevant for parsing AND interpreting it, but not for generating as we can always use the default) + - Content-ID is used to specify a id on each body respectivly which can be used to refer to it (e.g. in HTML) + - in html use e.g. `' ... ) + (image/png (Content-ID ) ... ) + ... )) + (image/png (Content-Disposition attachment) ...) + (image/png (Content-Disposition attachment) ...)) +``` + +Possible alternate structure: + +``` +(multipart/mixed + (multipart/related + + (multipart/alternative + (text/plain ... '[cid:contentid@1aim.com]' ... ) + (text/html ... '' ... ) ) + + (image/png (Content-ID ) ... ) ) + + (image/png (Content-Disposition attachment) ...) + (image/png (Content-Disposition attachment) ...)) +``` + +but I have not seen the `[cid:...]` for text/plain in any standard, through it might be there. +Also if se we might still have a related specific for the html (for html only stuff) so: +- place Embedding in Data in the outer `multipart/related` +- place Embedding returned by the template in inner `multipart/related` + +# Attatchment + +proposed filenames for attachments can be given through parameters of the disposition header + +it does not allow non ascii character there! + +see rfc2231 for more information, it extends some part wrt.: + +- splitting long parameters (e.g. long file names) +- specifying language and character set +- specifying language for encoded words + +# Encoded Words + +extended by rfc2231 + +additional limits in header fields + +header containing encoded words are limited to 76 bytes + +a "big" text chunk can be split in multiple encoded words seperated by b'\r\n ' + +non encoded words and encoded words can apear in the same header field, but +must be seperate by "linear-white-space" (space) which is NOT removed when +decoding encoded words + +encoded words can appear in: + +- `text` sections where `text` is based on RFC 822! (e.g. Content-Description ) + - in context of RFC 5322 this means `unstructured` count's as text +- `comments` (as alternative to `ctext`,`quoted-pair`,`comment` +- `word`'s within a `phrase` + +**Therefor it MUST NOT appear in any structured header field except withing a `comment` or `phrase`!** + +**You have to encode text which looks like an encoded word** + + + +limitations: + +- in comment's no ')',')' and '"' +- in headers no ' ' + + +# Other + +there is no `[CFWS]` after the `:` in Header fields, +but most (all?) of the parts following them are allowed +to start with a `[CFWS]`. (exception is unstructured where +a `CFWS` can be allowed but also MIGHT be part of the +string) + +CFWS -> (un-) foldable whitespace allowing comments +FWS -> (un-) foldable whitespace without comments + + +# Relevant RFCs +5321, 5322, 6854, 3492, 2045, 2046, 2047, 4288, 4289, 2049, 6531, 5890 + +make sure to not use the outdated versions + + +# Parsing Notes + +be strict when parsing (e.g. only ws and printable in subject line) + +if "some other" strings should still be supported do not do zero +copy, but instead add the data to a new buff _replacing invalid +chars with replacement symbol or just stripping them_ + + +# Non-utf8 Non-Ascci bytes in Mail body + +The mail body can contain non-utf8, non-ascii data (e.g. +utf16 data, images etc.) WITHOUT base64 encoding if +8BITMIME is supported (note there is also BINARY and CHUNKING) + +smtp still considers _the bytes_ corresponding to CR LF and DOT special. + +- there is a line length limit, lines terminate with b'CRLF' +- b'.CRLF' does sill end the body (if preceeded by CRLF, or body starts with it) + - so dot-staching is still done on protocol level + + + +## Hot to handle `obs-` parsings + +we have to be able to parse mails with obsolete syntax (theoretically) +but should never genrate such mails, the encder excepts its underlying +data to be correct, but it might not be if we directly place `obs-` +parsed data there. For many parts this is no problem as the +`obs-` syntax is a lot about having FWS at other positions, +_between components_ (so we won't have a problem with the encoder). +Or some additional obsolete infromations (which we often/allways can just +"skip" over). So we have to check if there are any braking cases and if +we have to not zero copy them when parsing but instead transform them +into a valide representation, in worst case we could add a `not_encodable` +field to some structs. + +# TODO +check if some parts are empty and error if encode is calde on them +e.g. empty domain + + +# Postponded + +`component::Disposition` should have a `Other` variant, using `Token` (which +means a general extension token type is needed) + +other features like signature, encryption etc. + +check what happens if I "execute" a async/mio/>tokio< +based future in a CPU pool? Does it just do live +polling in the thread? Or does it act more intelligent? +or does it simply fail? + +just before encoding singlepart bodies, resource is resolved, +therefore: + +1. we now have the Mime + File meta + TransferEncoding +2. add* ContentType header to headers +3. add* ContentTransferEncoding header to headers +4. add* file meta infor to ContentDisposition header if it exists +5. note that >add*< is not modifying Mail, but adds it to the list of headers to encode + + +warn when encoding a Disposition of kind Attachment which's +file_meta has no name set + + +// From RFC 2183: +// NOTE ON PARAMETER VALUE LENGHTS: A short (length <= 78 characters) +// parameter value containing only non-`tspecials' characters SHOULD be +// represented as a single `token'. A short parameter value containing +// only ASCII characters, but including `tspecials' characters, SHOULD +// be represented as `quoted-string'. Parameter values longer than 78 +// characters, or which contain non-ASCII characters, MUST be encoded as +// specified in [RFC 2184]. +provide a gnneral way for encoding header parameter which follow the scheme: +` *(";" "=" )` this are ContentType and ContentDisposition + for now + + +IF Item::Encoded only appears as encoded word, make it Item::Encoded word, +possible checking for "more" validity then noew + + +email::quote => do not escape WSP, and use FWS when encoding +also make quote, generally available for library useers a +create_quoted_string( .. ) + +# Dependencies + +quoted_printable and base64 have some problems: +1. it's speaking of a 76 character limit where it is 78 + it seems they treated the RFC as 78 character including + CRLF where the RFC speaks of 78 characters EXCLUDING + CRLF +2. it's only suited for content transfer encoding the body + as there is a limit of the length of encoded words (75) + which can't be handled by both + +also quoted_printable has another problem: +3. in headers the number of character which can be displayed without + encoding is more limited (e.g. no ' ' ) quoted_printable does not + respect this? (TODO CHECK THIS) + \ No newline at end of file diff --git a/src/char_validators.rs b/src/char_validators.rs new file mode 100644 index 0000000..6fbabe7 --- /dev/null +++ b/src/char_validators.rs @@ -0,0 +1,299 @@ + + +//TODO move MailType to types? +#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)] +pub enum MailType { + Ascii, + Internationalized +} + +impl MailType { + pub fn supports_utf8( &self ) -> bool { + use self::MailType::*; + match *self { + Ascii => false, + Internationalized => true + } + } +} +///WS as defined by RFC 5234 +#[inline(always)] +pub fn is_ws( ch: char ) -> bool { + // is not limited to ascii ws + //ch.is_whitespace() + //WSP = SP / HTAB + ch == ' ' || ch == '\t' +} + +#[inline(always)] +pub fn is_space( ch: char ) -> bool { + ch == ' ' +} + +#[inline(always)] +pub fn is_ascii_vchar( ch: char ) -> bool { + let u32_ch = ch as u32; + 32 < u32_ch && u32_ch < 128 +} + +//VCHAR as defined by RFC 5243 +#[inline(always)] +pub fn is_vchar( ch: char, tp: MailType ) -> bool { + use self::MailType::*; + match tp { + Ascii => is_ascii_vchar( ch ), + Internationalized => is_ascii_vchar( ch ) || ch.len_utf8() > 1 + } +} + +///any whitespace (char::is_whitespace +#[inline(always)] +pub fn is_any_whitespace(ch: char) -> bool { + ch.is_whitespace() +} + +//ctext as defined by RFC 5322 +pub fn is_ctext( ch: char, tp: MailType ) -> bool { + use self::MailType::*; + match ch { + '!'...'\'' | + '*'...'[' | + ']'...'~' => true, + // obs-ctext + _ => match tp { + Ascii => false, + Internationalized => ch.len_utf8() > 1 + } + } +} + +/// check if a char is a especial (based on RFC 5322) +pub fn is_special(ch: char ) -> bool { + match ch { + '(' | ')' | + '<' | '>' | + '[' | ']' | + ':' | ';' | + '@' | '\\'| + ',' | '.' | + '"' => true, + _ => false + } +} + +/// check if a char is an tspecial (based on RFC 2045) +pub fn is_tspecial( ch: char ) -> bool { + match ch { + '(' | ')' | + '<' | '>' | + '@' | ',' | + ';' | ':' | + '\\'| '"' | + '/' | '[' | + ']' | '?' | + '=' => true, + _ => false + } +} + + + +/// atext as defined by RFC 5322 +#[inline(always)] +pub fn is_atext( ch: char, tp: MailType ) -> bool { + use self::MailType::*; + ( ! is_special( ch ) ) || { + match tp { + Ascii => false, + Internationalized => ch.len_utf8() > 1 + } + } +} + +///dtext as defined by RFC 5322 +#[inline(always)] +pub fn is_dtext( ch: char , mt: MailType ) -> bool { + match ch as u32 { + 33...90 | + 94...126 => true, + _ => (mt == MailType::Internationalized && ch.len_utf8() > 1 ) + } +} + +//qtext as defined by RFC 5322 +pub fn is_qtext( ch: char, tp: MailType ) -> bool { + use self::MailType::*; + match ch { + //not ' ' [d:32] + '!' | + //not '"' [d:34] + '#'...'[' | + //not '\\' [d:92] + ']'...'~' => true, + //obs-qtext + _ => match tp { + Ascii => false, + Internationalized => ch.len_utf8() > 1 + } + } +} + +/// is it a CTL (based on RFC 822) +#[inline(always)] +pub fn is_ctl( ch: char ) -> bool { + (ch as u32) < 32 +} + + +#[inline(always)] +pub fn is_especial( ch: char ) -> bool { + match ch { + '(' | ')' | + '<' | '>' | + '@' | ',' | + ';' | ':' | + '"' | '/'| + '[' | ']' | + '?' | '.' | + '=' => true, + _ => false + } +} + + +//TODO thisshould be some where else I think +// (but it is used by `1. codec`, `2. components` ) +/// based on RFC 2047 +pub mod encoded_word { + use nom; + + use error::*; + + use super::{ is_especial, is_ascii_vchar }; + + #[derive(Debug, Copy, Clone, Hash, Eq, PartialEq)] + pub enum EncodedWordContext { + Phrase, + Text, + Comment + } + + impl EncodedWordContext { + + fn char_validator( &self ) -> fn(char) -> bool { + use self::EncodedWordContext::*; + match *self { + Phrase => valid_char_in_ec_in_phrase, + Text => is_encoded_word_char, + Comment => valid_char_in_ec_in_comment, + } + } + } + + + pub fn is_encoded_word( word: &str, ctx: EncodedWordContext ) -> bool { + try_parse_encoded_word_parts( word, ctx ).is_ok() + } + + pub fn try_parse_encoded_word_parts( word: &str, ctx: EncodedWordContext ) + -> Result<(&str, &str, &str)> + { + let char_validator = ctx.char_validator(); + // Note we could get a possible speed up by making rustc generate + // a different function for each Context, inlining ALL char tests + let res = do_parse!( + word, + char!( '=' ) >> + char!( '?' ) >> + charset: take_while!( is_ew_token_char ) >> + char!( '?' ) >> + encoding: take_while!( is_ew_token_char ) >> + char!( '?' ) >> + text: take_while!( char_validator ) >> + eof!() >> + (charset, encoding, text) + ); + + match res { + nom::IResult::Done( rest, result ) => { + //we used eof, so this should be true + assert!( rest.len() == 0 ); + Ok( result ) + }, + nom::IResult::Incomplete( .. ) => bail!( "incomplete encoded word: {:?}", word ), + nom::IResult::Error( e ) => bail!( "malformed encoded word: {:?}, {:?}", word, e ) + } + } + + fn is_encoded_word_char( ch: char ) -> bool { + is_ascii_vchar( ch ) && ch != '?' + } + + + fn valid_char_in_ec_in_comment( ch: char ) -> bool { + is_encoded_word_char( ch ) && !( ch == '(' || ch == ')' || ch == '"' ) + } + + fn is_ew_token_char( ch: char ) -> bool { + is_ascii_vchar( ch ) && !is_especial( ch ) + } + + + + + fn valid_char_in_ec_in_phrase( ch: char ) -> bool { + match ch { + '0'...'9' | + 'a'...'z' | + 'A'...'Z' | + '!' | '*' | + '+' | '-' | + '/' | '=' | + '_' => true, + _ => false + } + } + +} + +pub mod quoted_word { + use super::{ MailType, is_qtext, is_vchar, is_ws }; + + + pub fn is_quoted_word( qword: &str, tp: MailType ) -> bool { + let mut iter = qword.chars(); + if let Some('"') = iter.next() {} else { return false } + let mut next = iter.next(); + while let Some(ch) = next { + match ch { + '\\' => { + if let Some(next_char) = iter.next() { + if !( is_vchar( next_char, tp ) || is_ws( next_char ) ) { + return false; + } + } else { + return false; + } + }, + '"' => { + if iter.next().is_none() { + return true; + } else { + return false; + } + } + ch => { + if !is_qtext( ch, tp ) { + return false + } + } + } + next = iter.next() + } + + return false; + } +} + + + diff --git a/src/codec/mod.rs b/src/codec/mod.rs new file mode 100644 index 0000000..0b3e91c --- /dev/n