GithubHelp home page GithubHelp logo

honeybadger-io / incoming Goto Github PK

View Code? Open in Web Editor NEW
308.0 15.0 19.0 203 KB

Incoming! helps you receive email in your Rack apps.

Home Page: https://www.honeybadger.io/

License: MIT License

Ruby 100.00%

incoming's Introduction

Incoming!

Receive email in your Rack apps.

Incoming! receives a Rack::Request and hands you a Mail::Message, much like ActionMailer::Base.receive does with a raw email. We currently support the following services:

  • SendGrid
  • Mailgun
  • Postmark
  • CloudMailin
  • Mandrill
  • Any mail server capable of routing messages to a system command

Brought to you by ⚡ Honeybadger.io, painless Rails exception tracking.

Build Status Gem Version

Installation

  1. Add Incoming! to your Gemfile and run bundle install:

    gem "incoming"
  2. Create a new class to receive emails (see examples below)

  3. Implement an HTTP endpoint to receive HTTP post hooks, and pass the request to your receiver. (see examples below)

SendGrid Example

class EmailReceiver < Incoming::Strategies::SendGrid
  def receive(mail)
    %(Got message from #{mail.to.first} with subject "#{mail.subject}")
  end
end

req = Rack::Request.new(env)
result = EmailReceiver.receive(req) # => Got message from [email protected] with subject "hello world"

Sendgrid API reference

Mailgun Example

class EmailReceiver < Incoming::Strategies::Mailgun
  setup api_key: "asdf"

  def receive(mail)
    %(Got message from #{mail.to.first} with subject "#{mail.subject}")
  end
end

req = Rack::Request.new(env)
result = EmailReceiver.receive(req) # => Got message from [email protected] with subject "hello world"

Mailgun API reference

Postmark Example

class EmailReceiver < Incoming::Strategies::Postmark
  def receive(mail)
    %(Got message from #{mail.to.first} with subject "#{mail.subject}")
  end
end

req = Rack::Request.new(env)
result = EmailReceiver.receive(req) # => Got message from [email protected] with subject "hello world"

Postmark API reference

CloudMailin Example

Use the Raw Format when setting up your address target.

class EmailReceiver < Incoming::Strategies::CloudMailin
  def receive(mail)
    %(Got message from #{mail.to.first} with subject "#{mail.subject}")
  end
end

req = Rack::Request.new(env)
result = EmailReceiver.receive(req) # => Got message from [email protected] with subject "hello world"

CloudMailin API reference

Mandrill Example

Mandrill is capable of sending multiple events in a single webhook, so the Mandrill strategy works a bit differently than the others. Namely, the .receive method returns an Array of return values from your #receive method for each inbound event in the payload. Otherwise, the implementation is the same:

class EmailReceiver < Incoming::Strategies::Mandrill
  def receive(mail)
    %(Got message from #{mail.to.first} with subject "#{mail.subject}")
  end
end

req = Rack::Request.new(env)
result = EmailReceiver.receive(req) # => Got message from [email protected] with subject "hello world"

Mandrill API reference

Postfix Example

class EmailReceiver < Incoming::Strategies::HTTPPost
  setup secret: "6d7e5337a0cd69f52c3fcf9f5af438b1"

  def receive(mail)
    %(Got message from #{mail.to.first} with subject "#{mail.subject}")
  end
end

req = Rack::Request.new(env)
result = EmailReceiver.receive(req) # => Got message from [email protected] with subject "hello world"
# /etc/postfix/virtual
@example.com http_post

# /etc/mail/aliases
http_post: "|http_post -s 6d7e5337a0cd69f52c3fcf9f5af438b1 http://www.example.com/emails"

Qmail Example:

class EmailReceiver < Incoming::Strategies::HTTPPost
  setup secret: "6d7e5337a0cd69f52c3fcf9f5af438b1"

  def receive(mail)
    %(Got message from #{mail.to.first} with subject "#{mail.subject}")
  end
end

req = Rack::Request.new(env)
result = EmailReceiver.receive(req) # => Got message from [email protected] with subject "hello world"

To setup a global incoming email alias:

# /var/qmail/alias/.qmail-whoever - mails to whoever@ will be delivered to this alias.
|http_post -s 6d7e5337a0cd69f52c3fcf9f5af438b1 http://www.example.com/emails

Domain-specific incoming aliases can be set as follows:

#/var/qmail/control/virtualdomains
example.com:example

#~example/.qmail-whoever
|http_post -s 6d7e5337a0cd69f52c3fcf9f5af438b1 http://www.example.com/emails

Now mails to [email protected] will be posted to the corresponding URL above. To post all mails for example.com, just add the above line to ~example/.qmail-default.

Example Rails Controller

# app/controllers/emails_controller.rb
class EmailsController < ActionController::Base
  def create
    if EmailReceiver.receive(request)
      render json: { status: "ok" }
    else
      render json: { status: "rejected" }, status: 403
    end
  end
end
# config/routes.rb
Rails.application.routes.draw do
  post "/emails" => "emails#create"
end
# spec/controllers/emails_controller_spec.rb
require "spec_helper"

describe EmailsController, "#create" do
  it "responds with success when request is valid" do
    allow(EmailReceiver).to receive(:receive).and_return(true)
    post :create
    expect(response.success?).to eq(true)
    expect(response.body).to eq(%({"status":"ok"}))
  end

  it "responds with 403 when request is invalid" do
    allow(EmailReceiver).to receive(:receive).and_return(false)
    post :create
    expect(response.status).to eq(403)
    expect(response.body).to eq(%({"status":"rejected"}))
  end
end

TODO

  1. Provide authentication for all strategies where possible (currently only Mailgun requests are authenticated.)

Contributing

  1. Fork it.
  2. Create a topic branch git checkout -b my_branch
  3. Commit your changes git commit -am "Boom"
  4. Push to your branch git push origin my_branch
  5. Send a pull request

License

Incoming! is free software, and may be redistributed under the terms specified in the MIT-LICENSE file.

incoming's People

Contributors

abhas avatar andersonsp avatar dependabot-preview[bot] avatar dependabot[bot] avatar joshuap avatar kiwimanman avatar starrhorne avatar stympy avatar subzero10 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  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  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

incoming's Issues

Postfix in README

Postfix example in README is wrong, I think.
You cannot pipe mail from virtual. Instead you should map your virtual alias to a local alias, and then pipe. Something like this:

/etc/postfix/virtual:
[email protected]        local_alias

/etc/mail/aliases:
local_alias:        "|http_post -s 6d7e5337a0cd69f52c3fcf9f5af438b1 http://www.example.com/emails"

Sendgrid strategy decoding issue with Content-Transfer-Encoding: base64

I've noticed an encoding issue for incoming emails with a Content-Transfer-Encoding: base64 header when handled by the Sendgrid strategy. My assumption is that the message content (text as well as html) is already being decoded by Sendgrid. But Sendgrid still passes on the orig. Content-Transfer-Encoding header and the Sendgrid strategy then passes the header when instantiating the Mail::Message instance. When we then access the mail's text content via mail.text_part.body.decoded the content is garbled because the Mail code applies Base64 decoding again to the content that is already decoded by Sendgrid.

I'm trying to get official confirmation from Sendgrid on the handling of the Content-Transfer-Encoding header here: https://community.sendgrid.com/sendgrid/topics/webhook_api_and_content_transfer_encoding_base64.

Mandrill encoding errors

We use Mandrill to deliver our inbound emails and occasionally we'll get an encoding error with one of the email replies (expecting UTF-8 but get ASCII-8BIT).

Originally I thought it was an issue with Mandrill but they claim everything is converted to UTF-8 before being sent to the webhook. If they're converting the content to UTF-8 but leaving the original headers alone, wouldn't that screw up the decoding?

Here's what we're using to grab the body of the email:

def get_body(mail)
  if mail.multipart?
    if mail.text_part
      mail.text_part.decoded
    else
      mail.html_part.decoded
    end
  else
    mail.body.decoded
  end
end

Not sure if this is an issue with the mandrill strategy or just the way we're extracting the body of the message.

Rack:Request is not the same as ActionDispatch::Request

Notably, they handle attachments differently. I set up two servers for Mailgun to POST to. Here are the results:

You'll notice that in the pure Rack environment, attachment-1 is a hash. In Rails, it's an ActionDispatch::Http::UploadedFile object. Your code assumes you are passing a ActionDispatch::Http::UploadedFile object to the Mailgun receiver.

I just ran into this in my own project MultiMail. I handle both cases now by checking if attachment-1 is a hash.

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.