mikejsavage.co.uk / blog

RSS feed

08 Sep 2018 / Least effort image self-hosting

My requirements:

I already have mail clients on my PC/phone, and a mail server running on my VPS, so writing it as an MDA seemed like the least effort approach. It ended up being one line in smtpd.conf, 70 lines of python, and a DNS entry.

The smtpd.conf entry looks like this:

accept from any for domain "topsecret.mikejsavage.co.uk" virtual { "alsotopsecret" => mike } deliver to mda "mkgallery" as gallery

The subdomain and recipient kind of act as passwords, obviously that’s bogus but it’s good enough to keep the kids out.

And the python:

#! /usr/local/bin/python2

import base64
import datetime
import email
import errno
import hashlib
import os
import re
import subprocess
import sys

os.chdir( "/var/www/gallery" )
mail = email.message_from_file( sys.stdin )
now = datetime.datetime.now().strftime( "%Y-%m-%d" )

def try_mkdir( path ):
    try:
        os.makedirs( path )
    except OSError as e:
        if e.errno != errno.EEXIST:
            raise

try_mkdir( now )

# parse + save images from incoming mail
def save_image( part, ext ):
    body = base64.b64decode( part.get_payload() )
    checksum = hashlib.sha256( body ).hexdigest()
    filename = checksum + "." + ext
    fullpath = now + "/" + filename
    thumbpath = os.path.splitext( now + "/thumb_" + filename )[ 0 ] + ".jpg"

    f = open( fullpath, "w" )
    f.write( body )
    f.close()

    # generate thumbnail
    subprocess.call( [ "convert", fullpath, "-resize", "400x400>", "-quality", "80", "-auto-orient", thumbpath ] )

for part in mail.walk():
    ct = part.get_content_type()
    if ct == "image/jpeg":
        save_image( part, "jpg" )
    elif ct == "image/png":
        save_image( part, "png" )

# build a new gallery page
page = open( now + "/index.html", "w" )
for filename in os.listdir( now ):
    if filename.startswith( "thumb_" ):
        continue
    if filename.endswith( ".jpg" ) or filename.endswith( ".png" ):
        page.write( '<a href="{}"><img src="{}"></a><br>\n'.format( filename, "thumb_" + filename ) )
page.close()

# build new index
galleries = [ ]
for gallery in os.listdir( "." ):
    galleries.append( gallery )
galleries.sort( reverse = True )

index = open( "index.html", "w" )
for gallery in galleries:
    if re.match( r"\d{4}-\d{2}-\d{2}", gallery ):
        index.write( '<h3><a href="{}">{}</a></h3>\n'.format( gallery, gallery ) )
index.close()

Attaching images in the iOS mail client is kind of annoying and you can only send a few images at once because smtpd rejects huge messages, but it’s still better than everyone else’s image hosting services and I knocked it out in like a day so whatever.