Project

ukdah

0.0
The project is in a healthy, maintained state
A tolerant pure-Ruby parser for mail headers, MIME trees, attachments, mbox files, and threads.
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
 Dependencies
 Project Readme

Ukdah

Turn raw email and mbox files into readable MIME messages, without a mail client.

Gem version CI status Ruby 3.1 or newer MIT license

Features · Installation · Quick start · Mailboxes and threads · Scope and safety


Ukdah is a dependency-free Ruby parser for RFC 5322 messages and MIME bodies. It exposes headers, decoded text, attachments, diagnostics, and conversation structure without connecting to IMAP, POP, or SMTP. The name comes from ι Cancri and the Arabic ʿuqdah, “knot.”

Features

  • Folded headers, encoded words, address groups, and RFC 2231 parameters
  • Multipart MIME trees with base64 and quoted-printable transfer decoding
  • Attachment and inline-part discovery, including cid: identifiers
  • Tolerant parsing with diagnostics for damaged messages
  • mbox splitting, Message-ID threading, and quote segmentation
  • Injectable charset decoder; no runtime dependencies

Installation

Add gem "ukdah" to your Gemfile and run bundle install, or install directly:

gem install ukdah

Requires Ruby 3.1 or newer.

Quick start

require "ukdah"

raw = "From: Ada <ada@example.com>\r\n" \
      "Subject: Hello\r\n" \
      "Content-Type: text/plain; charset=UTF-8\r\n\r\n" \
      "A short note."

message = Ukdah::Message.parse(raw)
puts message.from.first.email           # => ada@example.com
puts message.subject                    # => Hello
puts message.decoded(message.text_part) # => A short note.
puts message.diagnostics

message.attachments returns MIME parts with decoded transfer bodies. Treat their filenames as untrusted input; choose a safe destination name before writing one to disk.

Mailboxes and threads

messages = Ukdah::Mbox.each(File.binread("archive.mbox")).to_a
threads = Ukdah::Thread_.build(messages)

Mbox.each accepts bytes or an IO object and yields parsed messages. Thread_.build groups messages using Message-ID, References, and In-Reply-To.

Charset decoding

Pass a decoder when the application has its own charset strategy:

text = message.decoded(message.text_part, decoder: ->(bytes, charset) {
  bytes.force_encoding(charset || "UTF-8").encode("UTF-8", invalid: :replace, undef: :replace)
})

Without a custom decoder, Ukdah uses Ruby's Encoding support.

Scope and safety

Ukdah parses messages only; it does not fetch, send, or sanitize HTML mail. Sanitize message.html_part before display. For design rationale, see parsing-only scope and decoder injection.

Development

bundle install
bundle exec rake
gem build --strict ukdah.gemspec

License

MIT