{"id":22656913,"url":"https://github.com/icelk/smtp-filter","last_synced_at":"2025-03-29T08:11:27.281Z","repository":{"id":168032354,"uuid":"643653572","full_name":"Icelk/smtp-filter","owner":"Icelk","description":"Framework for making fast Postfix SMTP filters (after-queue)","archived":false,"fork":false,"pushed_at":"2023-05-27T19:08:17.000Z","size":8,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-02-03T21:47:35.719Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Icelk.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-05-21T20:47:39.000Z","updated_at":"2023-05-21T20:48:02.000Z","dependencies_parsed_at":null,"dependency_job_id":"387e9539-89d8-4487-b045-79d6cc4a6d1c","html_url":"https://github.com/Icelk/smtp-filter","commit_stats":null,"previous_names":["icelk/smtp-filter"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Icelk%2Fsmtp-filter","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Icelk%2Fsmtp-filter/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Icelk%2Fsmtp-filter/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Icelk%2Fsmtp-filter/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Icelk","download_url":"https://codeload.github.com/Icelk/smtp-filter/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":246156419,"owners_count":20732397,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-12-09T10:16:52.026Z","updated_at":"2025-03-29T08:11:27.276Z","avatar_url":"https://github.com/Icelk.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# smtp-filter\n\n`smtp-filter` is a framework for making fast\n[Postfix SMTP filters](https://www.postfix.org/FILTER_README.html) (after-queue) in Rust.\n\nMail filters can modify all the elements of mails, enabling the following (not exhaustive list):\n\n-   Custom, programmable mailing lists\n-   Adding or changing content in the body\n-   Virus checks\n-   Modifying the header fields (changing receiver, subject (e.g. remove \"Re: \"), hiding user-agent)\n-   Triggering back-end events from any incoming mail\n\n## Example\n\n```rust\nuse std::io::Write;\n\nuse mailparse::SingleInfo;\nuse smtp_filter::{utils, BasicMail, Filter, RecipientDisclosure, UnparsedMail};\n\nfn main() {\n    // log to file when built with --release\n    #[cfg(not(debug_assertions))]\n    let log_file = std::fs::File::create(\"/var/spool/filter/mail.log\").unwrap();\n\n    let mut logger = env_logger::builder();\n    logger\n        .filter_level(log::LevelFilter::Info)\n        .parse_default_env();\n    #[cfg(not(debug_assertions))]\n    logger.target(env_logger::Target::Pipe(Box::new(log_file)));\n    logger.init();\n\n    let mut filter = Filter::new();\n    filter\n        // if mail is to someone at `icelk.dev`\n        .filter(|mail: \u0026mut UnparsedMail| mail.header_domain() == Some(\"mydomain.org\"))\n        // if there are exactly 1 recipient\n        .filter(|mail| mail.header_recipients().count_addrs() == 1)\n        .and_then(|mail| {\n            let sender = utils::iter_addrs(mail.sender()).next();\n            // only allow mail from \"special@myotherdomain.org\"\n            let authorized = sender.map_or(false, |s| s.addr == \"special@myotherdomain.org\");\n            // extract the user of the recipient\n            let recip = utils::iter_addrs(mail.header_recipients())\n                .next()\n                .unwrap()\n                .addr\n                .strip_suffix(\"@mydomain.org\")\n                .to_owned();\n            match recip {\n                // reject mail if not from special mail address\n                _ if !authorized =\u003e Err(smtp_filter::Error::unauthorized()),\n                Some(\"some-user\") =\u003e {\n                    // re-route mail to other emails\n                    mail.set_recipient(\n                        utils::addr_list_from_iter(\n                            [\n                                SingleInfo {\n                                    addr: \"info@myotherdomain.org\".into(),\n                                    display_name: None,\n                                },\n                                SingleInfo {\n                                    addr: \"accounting@mydomain.org\".into(),\n                                    display_name: Some(\"Accounting\".into()),\n                                },\n                            ]\n                            .into_iter(),\n                        ),\n                        // Show all other recipients to all recipients, so you can continue the thread\n                        RecipientDisclosure::Open,\n                    );\n                    Ok(())\n                }\n                _ =\u003e Ok(()),\n            }\n        });\n    // read mail\n    let mail = UnparsedMail::from_stdin().unwrap();\n    match filter.process(mail) {\n        Ok((mail, from, to)) =\u003e {\n            // send the mail back to postfix\n            let mut child = std::process::Command::new(\"sendmail\")\n                .args([\"-f\", \u0026from.to_string(), \"--\", \u0026to.to_string()])\n                .stdin(std::process::Stdio::piped())\n                .spawn()\n                .expect(\"failed to run sendmail\");\n            let mut stdin = child.stdin.take().unwrap();\n            stdin\n                .write_all(\u0026mail)\n                .expect(\"failed to send mail to sendmail\");\n            stdin.flush().unwrap();\n            drop(stdin);\n            let status = child.wait().expect(\"sendmail failed\");\n            std::process::exit(status.code().unwrap_or(0));\n        }\n        Err(s) =\u003e {\n            // if err, reject mail\n            println!(\"{s}\");\n            std::process::exit(1);\n        }\n    }\n}\n\n```\n\n## TODO\n\n-   `set_header` with new headers in `UnparsedMail`\n-   `ParsedMail`\n-   Bin feature which handles logging \u0026 spawn sendmail\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ficelk%2Fsmtp-filter","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ficelk%2Fsmtp-filter","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ficelk%2Fsmtp-filter/lists"}