{"id":17495737,"url":"https://github.com/iraikov/chicken-smtp","last_synced_at":"2026-01-06T04:49:59.401Z","repository":{"id":148366665,"uuid":"149707974","full_name":"iraikov/chicken-smtp","owner":"iraikov","description":"Parser combinators and state machine for Simple Mail Transfer Protocol (RFC 5321).","archived":false,"fork":false,"pushed_at":"2019-05-22T05:25:22.000Z","size":29,"stargazers_count":2,"open_issues_count":0,"forks_count":1,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-03-25T18:51:53.508Z","etag":null,"topics":["mta","parser-combinators","scheme","smtp","smtp-server"],"latest_commit_sha":null,"homepage":null,"language":"Scheme","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/iraikov.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","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":"2018-09-21T04:02:48.000Z","updated_at":"2023-10-01T03:48:29.000Z","dependencies_parsed_at":"2023-05-13T21:30:35.278Z","dependency_job_id":null,"html_url":"https://github.com/iraikov/chicken-smtp","commit_stats":null,"previous_names":[],"tags_count":3,"template":false,"template_full_name":null,"purl":"pkg:github/iraikov/chicken-smtp","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/iraikov%2Fchicken-smtp","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/iraikov%2Fchicken-smtp/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/iraikov%2Fchicken-smtp/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/iraikov%2Fchicken-smtp/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/iraikov","download_url":"https://codeload.github.com/iraikov/chicken-smtp/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/iraikov%2Fchicken-smtp/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28221917,"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","status":"online","status_checked_at":"2026-01-06T02:00:07.049Z","response_time":56,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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":["mta","parser-combinators","scheme","smtp","smtp-server"],"created_at":"2024-10-19T14:26:10.175Z","updated_at":"2026-01-06T04:49:59.375Z","avatar_url":"https://github.com/iraikov.png","language":"Scheme","funding_links":[],"categories":[],"sub_categories":[],"readme":"# smtp\n\n## Description\n\n`smtp` is a collection of parser combinators and state machine\nprimitives for the grammar defined in\nRFC 5321 (http://www.ietf.org/rfc/rfc5321.txt)\n(Simple Mail Transfer Protocol).\n\n## Data Types for SMTP Commands\n\nRepresenation of SMTP commands:\n\n```scheme\n (define-datatype cmd cmd?\n  (Helo (s string?))\n  (Ehlo (s string?))\n  (MailFrom (m mailbox?) (parameters list?))\n  (RcptTo   (m mailbox?) (parameters list?))\n  (Data)\n  (Rset)\n  (Send  (m mailbox?))\n  (Soml  (m mailbox?))\n  (Saml  (m mailbox?))\n  (Vrfy  (s string?))\n  (Expn  (s string?))\n  (Help  (s string?))\n  (Noop)\n  (Quit)\n  (Turn)\n  (WrongArg (cmd string?)  (message string?)))\n```\n\nThe record printer defined for this datatype prints values of this\ntype in a format conforming to the RFC. For example:\n\n```\n  csi\u003e (print (Helo \"myhost.org\"))\n  HELO myhost.org\n```\n\nThe `mailbox` datatype has the following definition:\n\n```\n (define-datatype mailbox mailbox?\n   (Mailbox (local-part string?) \n            (domain string?)))\n```\n\n## Data Types for SMTP Replies \n\nRepresentation of SMTP replies:\n\n```scheme\n  (define-datatype reply reply?\n   (Reply (code code?) (msg list?)))\n```\n\nAn SMTP reply is a three-digit return code plus comments. This is what\nthe list of strings is for; one string per line in the reply.  the\nrecord printer will append an CRLF end-of-line marker to each entry in\nthat list, so that the resulting string is ready to be sent back to\nthe peer.\n\n\nFor example:\n\n```\n \u003e (print (Reply (Code (Success) (MailSystem) 0)\n                     (list \"worked\" \"like\" \"a charm\")))\n 250-worked\n 250-like\n 250 a charm\n```\n\nThe `code` datatype consists of success code, category and\nsupplemental code:\n\n```scheme\n (define-datatype code code?\n   (Code (suc success-code?) (cat category?) (num integer?)))\n```\n\nIn addition, the `success-code` and `category` datatypes can be\nused to map symbolic identifiers to integers and vice versa:\n\n```scheme\n (define-enumerated-type \n   success-code success-code? success-vector \n   success-code-inject success-code-project \n   (Unused)\n   (PreliminarySuccess)\n   (Success)\n   (IntermediateSuccess)\n   (TransientFailure)\n   (PermanentFailure))\n\n (define-enumerated-type \n   category category? category-vector\n   category-inject category-project\n   (Syntax)\n   (Information)\n   (Connection)\n   (Unspecified3)\n   (Unspecified4)\n   (MailSystem))\n```\n\n`define-enumerated-type` defines a new record type, with as many\ninstances as there are instance names. `name-vector` is bound to a\nvector containing the instances of the type in the same order as the\n`instance-name` list. `name-inject` and `name-project` are\nprocedures that map integers to an instance and vice versa.\n\n## ESMTP State Machine\n\n\u003cprocedure\u003e(start-session)\u003c/procedure\u003e \n\nProcedure `start-session` returns an ESMTP state machine object (a\nprocedure), which takes in a stream containing an SMTP command and\nreturns an appropriate `session-fsm` value:\n\n  (define-datatype session-fsm session-fsm?\n    (Event (ev event?))\n    (Trans (ev event?) (fsm procedure?)))\n\nA stream in this case is defined as the representation used by the\n[[abnf]] library.\n\nThe `Event` variant signals an event that must be processed by the\ncalling library, while `Trans` signals an event and a state machine\ntransition. The following events can be returned by this state\nmachine:\n\n```scheme\n (define-datatype event event?\n   (SayHelo       (s string?))\n   (SayHeloAgain  (s string?))\n   (SayEhlo       (s string?))\n   (SayEhloAgain  (s string?))\n   (SetMailFrom   (m mailbox?) (parameters? list))\n   (AddRcptTo     (m mailbox?) (parameters? list))\n   (StartData)\n   (NeedHeloFirst)\n   (NeedMailFromFirst)\n   (NeedRcptToFirst)\n   (NotImplemented) ;; Turn, Send, Soml, Saml, Vrfy, Expn.\n   (ResetState)\n   (SayOK)\n   (SeeksHelp    (s string?))\n   (Shutdown)\n   (SyntaxErrorIn (s string?))\n   (Unrecognized  (s string?)))\n```\n\n\n## Examples\n\n```scheme\n\n;; An example MTA implementation\n\n(import datatype smtp abnf)\n\n(define domain    \"example.net\")\n(define host      \"chicken-mta\")\n(define mailfrom  (make-parameter #f))\n(define rcpto     (make-parameter '()))\n(define data      (make-parameter #f))\n\n(define (handle-event ev)\n  (cases event ev\n\t (SayHelo (s)\n\t  (Reply (Code (Success) (MailSystem) 0)\n\t\t (list host \" \" \"Hello \" s)))\n\t \n\t (SayHeloAgain (s)\n\t  (Reply (Code (Success) (MailSystem) 0)\n\t\t (list host \" \" \"Hello \" s)))\n\n\t (SayEhlo (s)\n\t  (Reply (Code (Success) (MailSystem) 0)\n\t\t (list host \" \" \"Hello \" s)))\n\t \n\t (SayEhloAgain (s)\n\t  (Reply (Code (Success) (MailSystem) 0)\n\t\t (list host \" \" \"Hello \" s)))\n\t \n\t (SetMailFrom (m)\n\t   (mailfrom m)\n\t   (Reply (Code (Success) (MailSystem) 0) \n\t\t  (list \"OK\")))\n\n\t (AddRcptTo (m)\n\t    (if (not (mailfrom))\n\t       (Reply (Code (PermanentFailure) (Syntax) 3)\n\t   \t      (list \"command out of sequence\"))\n\t       (begin\n\t\t (rcpto (cons m (rcpto)))\n\t\t (Reply (Code (Success) (MailSystem) 0) \n\t\t\t(list \"Accepted\")))))\n\n\t (StartData ()\n\t    (if (not (rcpto))\n\t       (Reply (Code (PermanentFailure) (MailSystem) 4)\n\t   \t      (list \"no valid recipients\"))\n\t       (begin\n\t\t (data (list))\n\t\t (Reply (Code (IntermediateSuccess) (MailSystem) 4)\n\t\t\t(list \"Ready\")))))\n\n\t (NeedHeloFirst ()\n\t   (Reply (Code (PermanentFailure) (Syntax) 3)\n\t   \t      (list \"command out of sequence: \"\n\t\t\t    \"need HELO first\")\n\t\t      ))\n\n\t (NeedMailFromFirst ()\n\t   (Reply (Code (PermanentFailure) (Syntax) 3)\n\t   \t      (list \"command out of sequence: \"\n\t\t\t    \"need MAIL first\")\n\t\t      ))\n\n\t (NeedMailRcptToFirst ()\n\t   (Reply (Code (PermanentFailure) (Syntax) 3)\n\t   \t      (list \"command out of sequence: \"\n\t\t\t    \"need RCPT first\")\n\t\t      ))\n\n\t (NotImplemented ()\n\t   (Reply (Code (PermanentFailure) (Syntax) 2)\n\t\t  (list \"command not implemented\")))\n\n\n\t (ResetState ()\n\t     (mailfrom #f)\n\t     (rcpto    #f)\n\t     (data     #f)\n\t     (Reply (Code (Success) (MailSystem) 0) \n\t\t    (list \"Reset OK\")))\n\n\t (SayOK ()\n\t     (Reply (Code (Success) (MailSystem) 0) \n\t\t    (list \"OK\")))\n\n\t (SeeksHelp (s)\n\t     (Reply (Code (Success) (Information) 4) \n\t\t    (list \"Commands supported:\"\n\t\t\t  \"HELO EHLO MAIL RCPT DATA QUIT RSET NOOP HELP\")))\n\n\t (Shutdown ()\n\t    (Reply (Code (Success) (MailSystem) 1)\n\t\t   (list host \" closing connection\")))\n\n\t (SyntaxErrorIn (s)\n\t    (Reply (Code (PermanentFailure) (Syntax) 1)\n\t\t   (list \"syntax error in \" s)))\n\n\t (Unrecognized (s)\n\t    (Reply (Code (PermanentFailure) (Syntax) 0)\n\t\t   (list \"Unrecognized \" s)))\n\t ))\n\n;; from SSAX lib\n(define (peek-next-char port)\n  (read-char port) \n  (peek-char port))\n\n(define (read-smtp-line port)\n  (let loop ((cs (list)))\n    (let ((c (peek-char port)))\n    (if (eof-object? c) (reverse cs)\n\t(let ((n (peek-next-char port)))\n\t  (cond ((and (eq? n #\\newline) (eq? c #\\return))\n\t\t (begin\n\t\t   (read-char port)\n\t\t   (reverse (cons* n c cs)))\n\t\t )\n\t\t(else (loop (cons c cs)))))))))\n\n(define data-end (list #\\. #\\return #\\newline))\n      \n(define (handle-data in out cont)\n  (let loop ((tempdata (list)))\n    (let ((line (read-smtp-line in)))\n      (if (equal? line data-end)\n\t  (begin (data (reverse tempdata))\n\t\t (fprintf out \"~A\" \n\t\t\t  (Reply (Code (Success) (MailSystem) 0) (list \"OK\")))\n\t\t (cont)) \n\t  (loop (cons (list-\u003estring line) tempdata))))))\n\n(define (main in out)  \n  (let loop ((fsm (start-session)))\n    (let ((line     (read-smtp-line in)))\n      (if (null? line) (loop fsm)\n\t  (let ((instream (list `(() ,line))))\n\t    (let-values\n\t     (((reply ev fsm)\n\t       (cases session-fsm (fsm instream)\n\t\t      (Event (ev)\n\t\t\t     (let ((reply (handle-event ev)))\n\t\t\t       (values reply ev fsm)))\n\t\t      (Trans (ev fsm)\n\t\t\t     (let ((reply (handle-event ev)))\n\t\t\t       (values reply ev fsm))))))\n\t     (fprintf out \"~A\" reply)\n\t     (cases event ev\n\t\t    (StartData ()\n\t\t\t       (handle-data in out (lambda () (loop fsm))))\n\t\t    (Shutdown ()\n\t\t\t      (begin))\n\t\t    (else (loop fsm)))))))))\n\t\t     \n```\n\n## Version History\n\n- 5.0-5.1 Ported to CHICKEN 5\n- 3.2 Ensure test script returns proper exit status\n- 3.1 Compatibility with improved CharLex-\u003eCoreABNF constructor\n- 3.0 Compatibility with abnf 5\n- 2.1 Small fixes in the predicates of the RcptTo and MailFrom constructors\n- 2.0 Implemented typeclass interface\n- 1.0 Initial release\n\n## License\n\nBased on the Haskell Rfc2821 module by Peter Simons.\n\n  Copyright 2009-2019 Ivan Raikov.\n\n\n  This program is free software: you can redistribute it and/or\n  modify it under the terms of the GNU General Public License as\n  published by the Free Software Foundation, either version 3 of the\n  License, or (at your option) any later version.\n\n  This program is distributed in the hope that it will be useful, but\n  WITHOUT ANY WARRANTY; without even the implied warranty of\n  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n  General Public License for more details.\n\n  A full copy of the GPL license can be found at\n  \u003chttp://www.gnu.org/licenses/\u003e.\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Firaikov%2Fchicken-smtp","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Firaikov%2Fchicken-smtp","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Firaikov%2Fchicken-smtp/lists"}