Skip to content

Instantly share code, notes, and snippets.

@zealot128
Last active October 5, 2022 15:39
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save zealot128/fc2b2dc50ec5ce1190c1fbd9ecc0eeeb to your computer and use it in GitHub Desktop.
Save zealot128/fc2b2dc50ec5ce1190c1fbd9ecc0eeeb to your computer and use it in GitHub Desktop.
Papercut simple replacement = IMAP to print

Papercut replacement

  • imap to print
  • use case: on a Raspi oder Office server that hangs in the same LAN like the printer and can print via cups/system using "lp"
  • just uses lp under the hood
  • uses the awesome mail_room Gem under the hood to handle imap connection, which does most of the lifting

Send Mail as a authorized return-path (via regex matching) to your mailbox, it will print all pdf Attachments.

USAGE

create config.yml

---
:email: "print@mycompany.de"
:password: "securepassword"
:host: 'imap.mycompany.de'
:port: 993
:ssl: true
:start_ssl: false
:delete_after_delivery: true
:expunge_deleted: true
:name: "inbox"
:search_command: 'NEW'
:delivery_method: "print_attachments"
:delivery_options:
  :whitelist_from: ".*@mycompany.de$"
  # Modify lp command to change media format, or address, see lp -h for all options
  :lp_command: "lp -o media=a4 -o sides=two-sided-long-edge"
ruby run.rb

Or wrap with your favourite init tool.

---
:email: "printing@mycompany.de"
:password: "XXXXX"
:host: 'mail.mycompany.de'
:port: 993
:ssl: true
:start_ssl: false
:delete_after_delivery: true
:expunge_deleted: true
:name: "inbox"
:search_command: 'NEW'
:delivery_method: "print_attachments"
:delivery_options:
:whitelist_from: ".*@mycompany.de$"
:lp_command: "lp -o media=a4 -o sides=two-sided-long-edge"
[Unit]
Description=IMAP Print
After=syslog.target network.target
# DefaultMemoryAccounting=yes
[Service]
WorkingDirectory=/home/pludoni/cli-tools/print_fetcher
ExecStart=/bin/bash -l -c "cd /home/pludoni/cli-tools/print_fetcher/ && ruby run.rb"
CPUAccounting = yes
MemoryAccounting = yes
User=pludoni
Group=pludoni
UMask=0002
# if we crash, restart
RestartSec=20
Restart=always
# StartLimitIntervalSec=0
# output goes to /var/log/syslog
StandardOutput=syslog
StandardError=syslog
# This will default to "bundler" if we don't specify it
SyslogIdentifier=imap-print
[Install]
WantedBy=multi-user.target
require 'bundler/inline'
gemfile do
source "https://rubygems.org"
gem 'mail'
gem 'mail_room'
gem 'pry'
end
require 'open3'
module MailRoom
module Delivery
class PrintAttachments
Options = Class.new do
def initialize(config)
@config = config.delivery_options
@logger = config.logger
super()
end
attr_reader :config
attr_reader :logger
def lp_command
@config[:lp_command] || "lp"
end
def whitelisted?(mail)
return true if @config[:whitelist_from].nil? || @config[:whitelist_from] == ""
regex = Regexp.new(@config[:whitelist_from])
mail.return_path[regex]
end
end
# build a new delivery, do nothing
def initialize(config)
@config = config
end
# accept the delivery, do nothing
def deliver(*args)
mail = Mail.new(args.first)
if @config.whitelisted?(mail)
print!(mail)
else
@config.logger.warn "unpermitted return-path #{mail.return_path} #{mail.message_id}"
end
true
end
def print!(mail)
attachments = mail.attachments.select { |i| i.content_type['application/pdf'] }
if attachments.length == 0
@config.logger.warn "No pdf attachments found in mail #{mail.message_id}"
return
end
attachments.each do |pdf|
print_attachment(pdf)
end
end
def print_attachment(pdf)
save_name = pdf.filename.gsub(/[^\w\.]+/, '-')
tf = Tempfile.new(['mail_room_print_attachments', save_name])
tf.binmode
tf.write pdf.body.decoded
tf.rewind
command = "#{@config.lp_command} #{Shellwords.escape(tf.path)}"
stdout, stderr, status = Open3.capture3(command)
if status.success?
@config.logger.info("Printing #{save_name}: #{command}")
else
@config.logger.error("Printing failed: #{stderr}")
end
end
end
end
end
class MailRoom::Mailbox
def delivery_klass
MailRoom::Delivery::PrintAttachments
end
end
mailboxes = [
mb = MailRoom::Mailbox.new(YAML.load_file('config.yml'))
]
logger = Logger.new(STDOUT)
logger.formatter = proc do |severity, datetime, progname, msg|
"#{severity} #{datetime.strftime("%Y-%m-%dT%H:%M:%S")}: #{msg}\n"
end
mailboxes.first.instance_variable_set("@logger", logger)
$coordinator = MailRoom::Coordinator.new(mailboxes)
def start
Signal.trap(:INT) do
$coordinator.running = false
end
Signal.trap(:TERM) do
exit
end
puts "Starting MailRoom Server"
$coordinator.run
rescue Exception => e # not just Errors, but includes lower-level Exceptions
p e
puts e.backtrace
exit
end
start
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment