#!/bin/sh
##
## mbox2maildir - Convert a mailbox from mbox(5) into maildir(5) format
##                by using formail(1) and safecat(1)
## Copyright (c) 2002-2006 SATOH Fumiyasu, All rights reserved.
##
## License: GNU General Public License version 3
## Date: 2007-07-18, since 2002-03-26
##

## Required external commands:
## 	formail (included in procmail)
## 	safecat

set -u
umask 0077

cmd_name="mbox2maildir"
cmd_usage="Usage: $0 [OPTIONS] MAILDIR <MBOX

Options:
 -F	Do not remove the \"From \" line from each messages
 -u	Migrate X-UIDL: header
 -e	Escape bad characters in UIDL instead of ignoring
 -s	Migrate Status: header
"

## Functions
## ----------------------------------------------------------------------

echo_e()
{
    echo "$cmd_name: error: $@" 1>&2
}

echo_w()
{
    echo "$cmd_name: warning: $@" 1>&2
}

## Check options
## ======================================================================

flag_remove_fromline="yes"
flag_migrate_uidl=""
flag_escape_uidl=""
flag_migrate_status=""

while getopts Fues opt; do
    case "$opt" in
    F)
	flag_remove_fromline=""
	;;
    u)
	flag_migrate_uidl="yes"
	;;
    e)
	flag_escape_uidl="yes"
	;;
    s)
	flag_migrate_status="yes"
	;;
    *)
	exit 100
	;;
    esac
done
shift `expr $OPTIND - 1`

if [ $# -ne 1 ]; then
    echo "$cmd_usage"
    exit 0
fi
maildir="$1"; shift

if [ -d "$maildir" ]; then
    :
else
    echo_e "specified maildir is not directory: $maildir"
    exit 100
fi

if [ -d "$maildir/tmp" -a -d "$maildir/new" -a -d "$maildir/cur" ]; then
    :
else
    echo_e "specified directory is not maildir: $maildir"
    exit 100
fi

if [ -w "$maildir/tmp" -a -w "$maildir/new" -a -w "$maildir/cur" ]; then
    :
else
    echo_e "specified maildir is not writeable: $maildir"
    exit 100
fi

## Check commands
## ======================================================================

if type formail >/dev/null 2>&1; then
    :
else
    echo_e "formail(1) command does not found in \$PATH"
    exit 100
fi

if type safecat >/dev/null 2>&1; then
    :
else
    echo_e "safecat(1) command does not found in \$PATH"
    exit 100
fi

## ======================================================================

## NOTE: See RFC 2076 (3.16 Miscellaneous) about Status: header field.
## NOTE: See http://cr.yp.to/proto/maildir.html about maildir(5) format.

formail \
    -bf \
    ${flag_remove_fromline:+-I'From '} \
    -s safecat "$maildir/tmp" "$maildir/new" \
|while read uidl; do
    dst="$maildir/new/$uidl"

    ## X-UIDL: header migration hack
    if [ -n "$flag_migrate_uidl" ]; then
	xuidl="`formail -x 'X-UIDL:' <"$dst" |head -1 |sed 's/^ *//'`"
	if [ -n "$flag_escape_uidl" ]; then
	    xuidl="`echo "$xuidl" |sed 's#\\#\\5C#g;s#/#\\2F#g;s#:#\\3A#g'`"
	fi
	if [ -n "$xuidl" ] && echo "$xuidl" |grep -v '[/:]' >/dev/null; then
	    uidl="$xuidl"
	    dst_new="$maildir/new/$uidl"
	    if [ ! -f "$dst_new" ]; then
		mv "$dst" "$dst_new"
		dst="$dst_new"
	    else
		echo_w "X-UIDL conflicts with another one: $xuidl: $dst"
	    fi
	else
	    echo_w "X-UIDL has wrong character for name in maildir: $xuidl: $dst"
	fi
    fi

    ## Status: header migration hack
    if [ -n "$flag_migrate_status" ]; then
	status="`formail -x 'Status:' <"$dst"`"
	if expr "$status" : ".*R" >/dev/null; then
	    ## Read
	    dst_new="$maildir/cur/$uidl:2,S"
	    mv "$dst" "$dst_new"
	    dst="$dst_new"
	elif expr "$status" : ".*O" >/dev/null; then
	    ## Old
	    dst_new="$maildir/cur/$uidl:2,"
	    mv "$dst" "$dst_new"
	    dst="$dst_new"
	fi
    fi

    echo "$dst"
done

exit 0

