Giter Site home page Giter Site logo

kbrw / mailibex Goto Github PK

View Code? Open in Web Editor NEW
60.0 6.0 23.0 1.37 MB

Library containing Email related implementations in Elixir : dkim, spf, dmark, mimemail, smtp

License: MIT License

Elixir 83.30% C 3.63% Rich Text Format 13.07%
dkim elixir spf smtp mimemail dmarc decoding

mailibex's Introduction

mailibex Build Status

Library containing Email related implementations in Elixir : dkim, spf, dmark, mimemail, smtp

MimeMail

%MimeMail{headers: [{key::atom, {:raw,binary} | MimeMail.Header.t}], body: binary | [MimeMail.t] | {:raw,binary}}
# headers contains a keywordlist of either the raw form of the header, 
# or an encodable version of the header (a term with a module implementing MimeMail.Header.to_ascii)
# body contains either the raw binary body, the decoded body as a binary (using content-transfer-encoding), 
# or a list of MimeMail struct to encode in case of a multipart
# If body is a text, then the decoded body binary will be the UTF8 version of the text converted from the source charset
  • MimeMail.from_string parse the mimemail binary into a MimeMail struct explained above, with all the headers and body in their encoded form ({:raw,binary})
  • MimeMail.encode_headers(mail) apply the MimeMail.Header.to_ascii to every header to convert them into a {:raw,binary} form.
  • MimeMail.decode_headers(mail,[Mod1,Mod2]) applies successively Mod1.decode_headers(mail) the Mod2.decode_headers(mail) to the result.
  • MimeMail.encode_body(mail) encodes the mail body from a decoded form (binary | [MimeMail]) into a {:raw,binary}form
  • MimeMail.decode_body(mail) does the opposite.
  • MimeMail.to_string(mail) encode headers and body of a MimeMail into an ascii mail binary.

Currently, the library contains three types of acceptable header value (implementing MimeMail.Header) :

  • binary : simple binary headers are utf8 strings converted into encoded words
  • {value,%{}=params} are only acceptable tuple as header, converted into a 'content-*' style header ( value; param1=value1, param2=value2)
  • [%MimeMail.Address{name="toto", address: "[email protected]"}] : lists are encoded into mailbox list header
  • %DKIM{} : Converted into a dkim header

So for instance :

%MimeMail{headers: [
    to: [%MimeMail.Address{name: "You",address: "[email protected]"}],
    from: [%MimeMail.Address{name: "Me",address: "[email protected]"}],
    cc: "[email protected]", # only ascii so ok to encode it as a simple encoded word
    'content-type': {'text/plain',%{charset: "utf8"}},
  ],
  body: "Hello world"}
|> MimeMail.to_string

FlatMail

Flat mail representation of MimeMail is simply a KeywordList where all the keys [:txt,:html,:attach,:attach_in,:include] are used to construct the body tree of alternative/mixed/related multiparts in the body field of the MimeMail struct, and the rest of the KeywordList became the header field.

MimeMail.Flat.to_mail(from: "[email protected]", txt: "Hello world", attach: "attached plain text", attach: File.read!("attachedfile"))
|> MimeMail.to_string

Need more explanations here...

MimeTypes

ext2mime and mime2ext are functions generated at compilation time from the apache mime configuration file https://svn.apache.org/repos/asf/httpd/httpd/trunk/docs/conf/mime.types .

"image/png" = MimeTypes.ext2mime(".png")
".png" = MimeTypes.mime2ext("image/png")

bin2ext matches the begining of the binary and sometimes decode the binary in order to determine the file extension (then we can use ext2mimeto find the mime type if needed.

".webm" = MimeTypes.bin2ext(File.read!("path/to/my/webm/file.webm"))

DKIM

[rsaentry] =  :public_key.pem_decode(File.read!("test/mails/key.pem"))
mail = MimeMail.from_string(data)

mail = DKIM.sign(mail,:public_key.pem_entry_decode(rsaentry), d: "order.brendy.fr", s: "cobrason")
case DKIM.check(mail) do
  :none      ->IO.puts("no dkim signature")
  {:pass,{key,org}}      ->IO.puts("the mail is signed by #{key} at #{org}")
  :tempfail  -> IO.puts("the dns record is unavailable, try later")
  {:permfail,msg}->IO.puts("the sender is not authorized because #{msg}")
end

Need more explanations here...

DMARC

Organizational Domain implementation using public suffix database : (https://publicsuffix.org/list/effective_tld_names.dat)

"orga2.gouv.fr" = DMARK.organization "orga0.orga1.orga2.gouv.fr"

SPF

Full implementation of the Sender Policy Framework (https://tools.ietf.org/html/rfc7208).

case SPF.check("[email protected]",{1,2,3,4}, helo: "relay.com", server_domain: "me.com") do
  :none      ->IO.puts("no SPF information")
  :neutral   ->IO.puts("nor authorized neither not authorized")
  :pass      ->IO.puts("the sender is authorized")
  {:fail,msg}->IO.puts("the sender is not authorized because #{msg}")
  :softfail  ->IO.puts("not authorized but don't be rude")
  :temperror ->IO.puts("temporary error, try again latter")
  :permerror ->IO.puts("spf error, ask to remote admin")
end

Current Status

  • DKIM is fully implemented (signature/check), missing DKIM-Quoted-Printable token management
  • mimemail encoding/decoding of headers and body are fully implemented
  • flat mime body representation for easy mail creation / modification
  • DMARC implementation of organizational domains
  • SPF is fully implemented

TODO :

  • DMARC report
  • smtp client implementation
  • smtp server implementation over Ranch

CONTRIBUTING

Hi, and thank you for wanting to contribute. Please refer to the centralized informations available at: https://github.com/kbrw#contributing

mailibex's People

Contributors

antoinekbrw avatar antoinereyt avatar awetzel avatar guillaumemilan avatar heri16 avatar imnotavirus avatar maennchen avatar shakadak avatar simonj40 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

mailibex's Issues

Support Pure Elixir implementation of iconv (e.g. Codepagex)

NIFs should be generally avoided for production environments (unless they can be placed on a separate BEAM node). This is because NIFs causes scheduling problems in BEAM VM when there are hundreds of concurrent users.

Provide config option for user to avoid NIFs altogether.

Doesn't compile under 1.5.2

Apart from a bunch of compiler warnings about deprecated stuff,

== Compilation error in file lib/dmarc.ex ==
** (CompileError) Elixir.DMARC: function organization/1+79850:
  Internal consistency check failed - please report this bug.
  Instruction: {test,bs_test_tail2,{f,8268},[{x,14},0]}
  Error:       {no_bsm_context,{x,14}}:

    (stdlib) lists.erl:1338: :lists.foreach/2
    (stdlib) erl_eval.erl:670: :erl_eval.do_apply/6

I haven't been able to find out when/why this happens exactly yet, logging an issue so we can coordinate work if more people are looking at this.

warning: module attribute @shortdoc was set but never used

I'm using mailibex in my project and every day I see

warning: module attribute @shortdoc was set but never used
  /.../project/deps/mailibex/mix.exs:2

multiple times and it's annoying (because I keep thinking I introduced it).

Would you be opposed to its removal?

Server implementation

This project is nice, would like to contribute. It would be nice if you could publish a barebones smtp server and client implementation to get us started.

Cleanup repo

  • Replace TravisCI build badge with a working one
  • Create issues for existing TODO items in the README.md
  • Move / copy usage and reference documentation into the appropriate @moduledoc and @doc
  • Replace Mix.Task with a Makefile and elixir_make

Content-Type: multipart/mixed seems to force base64 encoding somehow in Dkim.sign(mail)

I have a huge message from aliexpress that when passed through Dkim.sign comes out encoded as base64 and gmail doesn't really like it. If i change the content type to "multipart/alternative" it doesn't end up as base64. I tried to shink it down but that doesn't impact it.

here's the test msg redacted a bit

Received: from out24-13.mail.alibaba.com (out24-13.mail.alibaba.com [115.124.24.13])
        by myemail.net (Postfix) with ESMTP id C0EE340438
        for [email protected]; Mon,  8 Nov 2021 14:05:19 +0000 (UTC)
DKIM-Filter: OpenDKIM Filter v2.10.3 myemail.net C0EE340438
Authentication-Results: myemail.net;
        dkim=pass (1024-bit key) header.d=mail.aliexpress.com [email protected] header.b=aqadJZh+
X-AliDM-RcptTo: YWI1MzdjMWE5NzRmMzQ0YzU4Yzc5ZjU1ZDRmYWI1NDk4QGJybmcudXM=
Feedback-ID: default:[email protected]:batch:13
DKIM-Signature:v=1; a=rsa-sha256; c=relaxed/relaxed;
        d=mail.aliexpress.com; s=s1024;
        t=1636380318; h=Date:From:To:Message-ID:Subject:MIME-Version:Content-Type;
        bh=ir/1fYnPPk+nKtectnb5YsqowhiW9rE7NTu4dGwaXeY=;
        b=aqadJZh+vBA1XUX2bye/ZDsukKYQaOOqpTb9NJVYc4qe+dKpNxs2rpdKQVFc/p0OouHn4NAmeRv3rRqzmS8TUdEAAMCplHhBoAEs9UN1nWLQ0M0YcM14O1SySV4W/5wTpAzM/Q7ytLXh4Yx/0DN/h00/6cA8NvDZX2LTlPo2xWI=
X-EnvId: 194285051284
Received: from ae-buyer-user9f74-xxfn8(mailfrom:[email protected] fp:SMTPD_----0owGdlt)
          by smtp.aliyun-inc.com(127.0.0.1);
          Mon, 08 Nov 2021 22:05:17 +0800
Date: Mon, 8 Nov 2021 06:05:17 -0800 (PST)
From: AliExpress <[email protected]>
To: [email protected]
Message-ID: 3197biz_price_reduce_edm:0:0_4659037$074e0a7ae314499f960e4e47fa388cfa
Subject: New price alert for bob
MIME-Version: 1.0
Content-Type: multipart/mixed;
        boundary="----=_Part_133763949_30120080.1636380317614"

------=_Part_133763949_30120080.1636380317614
Content-Type: text/html;charset=utf-8
Content-Transfer-Encoding: quoted-printable

<!--aeug_edm_19435#${houyiJobId}--><!doctype html>
<html xmlns=3D"http://www.w3.org/1999/xhtml" xmlns:v=3D"urn:schemas-microso=
ft-com:vml" xmlns:o=3D"urn:schemas-microsoft-com:office:office">
 <head>=20
  <meta name=3D"viewport" content=3D"width=3Ddevice-width, initial-scale=3D=
1">=20
 </head>=20
 <body style=3D"background: #fff; background-color: #fff;">=20
 
  </div> =20
 </body>
</html><div style=3D"display:none"><img style=3D"display:none" src=3D"http:=
//ae.mmstat.com/ae.edm.edm_open?tracelog=3Drowan-ae_usertouch-aeug_edm_1943=
5_1_en_US-2021-11-08&rowan_msg_id=3D3197biz_price_reduce_edm:0:0_4659037$07=
4e0a7ae314499f960e4e47fa388cfa"/></div>
------=_Part_133763949_30120080.1636380317614--

here's the code that calls the signing

def sign(mail_pipe) do
    
    if mail_pipe.domain == :error, do: raise "signing... domain error"
    
    [rsaentry] =  :public_key.pem_decode(File.read!(@privkey))   
    new_mail = %MimeMail{headers: mail_pipe.stripped_headers,body: mail_pipe.new_mm.body}
    
    to_addresses = Enum.map(mail_pipe.to_list, fn(x) ->
      x.new.address
    end)
    key = :public_key.pem_entry_decode(rsaentry)

    #
    #  Seems fine until this point and it gets base64 encoded
    #
    signed_mail = DKIM.sign(new_mail,key,d: mail_pipe.domain,s: "mail")
    
    raw_signed_mail = MimeMail.to_string(signed_mail)

    Idg.MP.print_report(mail_pipe)
    r = {mail_pipe.from.new.address,to_addresses,raw_signed_mail} 
  end

Compiler Warnings on Elixir 1.2.5

Compiling on Elixir 1.2.5 causes ugly compiler warnings that indicate bad code path.

Compiled lib/mimemail_headers.ex
lib/mime_types.ex:17: warning: this clause cannot match because a previous clause at line 17 always matches
lib/mime_types.ex:17: warning: this clause cannot match because a previous clause at line 17 always matches

Parser issues on "From"

Great library btw!!!!

The following is parsed incorrectly

X-Mailer: MIME::Lite 3.028 (F2.82; T1.31; A2.08; B3.13; Q3.13)
From: "craigslist - automated message, do not reply" <[email protected]>
To: [email protected]
Subject: craigslist.org:

here is the resulting parsed terms

[%MimeMail.Address{address: "\"craigslist - automated message", name: nil}, %MimeMail.Address{address: "[email protected]", name: "do not reply\""}]

New version on Hex.

@GuillaumeMilan
Hi, I saw that recently You've updated the package to resolve compile-time warnings, could you release a new version to hex? Or there is something needed to do that. If yes, can I help you some way?

Error during compulation

It does not seem to compile currently.

Steps:

  1. Create a new mix project.
  2. Add {:mailibex, "~> 0.1.4"}, to the deps.
  3. Run mix deps.get
  4. Run mix compile or iex -S mix, which crashes:
** (CompileError) Elixir.DMARC: function organization/1+81247:
  Internal consistency check failed - please report this bug.
  Instruction: {test,bs_test_tail2,{f,8408},[{x,14},0]}
  Error:       {no_bsm_context,{x,14}}:

    (stdlib) lists.erl:1338: :lists.foreach/2
    (stdlib) erl_eval.erl:670: :erl_eval.do_apply/6

Add support for Windows

Mailibex cannot be used on windows as MimeMail Iconv module fails to load due to missing iconv NIF dll.

Currently only iconv_nif.so is included in binary distribution on hex.pm.
Either include iconv_nif.dll into binary distribution on hex.pm, or update Mix compile instructions for Windows users.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.