#!/usr/bin/ruby -w
#
# Syntax:
# tnef_transform.rb [FILE]
#
# If FILE is a mail message that contains a winmail.dat, it is unpacked, its
# attachments are reattached to the message and the new message is spit to
# stdout.
#
# 2004 by Joshua Kwan <joshk@triplehelix.org>. This code is in the public
# domain, but it would be cool if you sent me any improvements.
#
# WARNING: As of Dec 17, 2004, a change to the rmail source is required
# for this code to work. See
#
# http://triplehelix.org/~joshk/message-delete_part.diff

require 'rmail';
require 'base64';
require 'mime/types';

fobj = [];

if ARGV.size > 0
	filename = ARGV[0];
	fobj = File.open(filename, 'r');
else
	fobj = STDIN;
end

# Back it up so we can spit it back out if necessary.
# Otherwise I'd just do Parser.read(fobj)
orig = fobj.read;

if fobj != STDIN
	fobj.close;
end

message = RMail::Parser.read(orig);

if not message.multipart?
	# Do nothing if not multipart (ie no winmail.dat)
	print orig;
	exit(0);
end

j = 0;

# Enter a tempdir
dirname = "";
dir = [];
i = 0;

begin
	dirname = sprintf("/tmp/tnef_transform%d.%d", $$, i);
	Dir.mkdir(dirname);
rescue
	i += 1;

	# wtf
	if i > 65535
		STDERR.puts "I give up! I can't create a temporary directory.";
		print orig;
		exit(1);
	end
	
	retry;
end

Dir.chdir(dirname);

# Keep track if there were actually any changes (new attachments) to the message.
done_something = 0;

message.each_part { |part|
	if part.header['Content-Type'] =~ /^application\/ms-tnef/
		tnef_pipe = IO.popen("tnef", "w+");
		
		if not tnef_pipe
			STDERR.puts "Could not decode, 'tnef' is not available!";
			print orig;
			exit(1);
		end

		# TNEF will do its thing and dump everything in our temp dir.
		tnef_pipe.print(part.decode);
		
		tnef_pipe.close;

		# Now, add the new parts to the message.
		d = Dir.open(".");

		d.each { |name|
			next if name == ".." or name == ".";
		
			nfobj = File.open(name, 'r');

			# First, base64 encode it :/
			encodednf = Base64.encode64(nfobj.read);

			npart = RMail::Message.new;
			
			# Determine MIME type for this.
			t = MIME::Types.type_for(name);

			if t.size
				npart.header.set('Content-Type', t[0], 'name' => name);
			else
				# lame fallback, but what can you do?
				npart.header.set('Content-Type', 'text/plain', 'name' => name);
			end

			npart.header.set('Content-Transfer-Encoding', 'base64');

			npart.body = encodednf;

			message.add_part(npart);

			nfobj.close;

			File.unlink(name);

			# Real change to be made
			done_something = 1;
		}

		# delete winmail.dat
		message.delete_part(j);
	end

	j += 1;
}

# doesn't matter where, we just need to get out
Dir.chdir("/");
Dir.rmdir(dirname);

if done_something == 1
	print message.to_s;
else
	print orig;
end

