#
# MH Mailbox support. We aren't using the python mhmail module
# because it's... really weird...
#
# Only partial support. we still use shelved headers. XXX
#

import string
import shelve
import marshal
import os
import os.path
import time
import glob
import pynemsg
from pyneheaders import *
from superbox import superbox

class mhbox(superbox):
	def __init__(self, user, box, prefix=None):
		# No prefix specified. Use UID
		if prefix == None:
			prefix = box.uid
		self.user = user
		self.mhbox_path = box.export_format[1]
		self.body_db = {}
		self.prefix = prefix
		if not os.path.exists(self.mhbox_path):
			os.mkdir(self.mhbox_path)
		if not os.path.isfile(prefix+".mhh"):
			# No headers database. Build it.
			self.header_db = shelve.open(prefix+".mhh")
			self.rebuild()
		else:
			self.header_db = shelve.open(prefix+".mhh")

	def rebuild(self):
		"""
		Rebuild message-id -> file number links.
		"""
		_t = time.time()
		total_files = glob.glob(self.mhbox_path+"/*")
		for filename in total_files:
			# Not an mh body file
			try:
				int(os.path.basename(filename))
			except ValueError:
				continue

			f = open(filename)
			body = f.read()
			f.close()
			# create new message object
			msg = pynemsg.pynemsg()
			# convert body from strange mhbox format
			end_head = string.find(body, "\n--------\n")
			msg.body = body[:end_head] + "\n\n" + body[end_head+10:]
			msg.parseheaders(self.user)
			
			if not msg.headers.has_key("message-id"):
				# no message-id exists: create one and make link
				msg_id = self.get_unique_message_id()
				self.body_db[msg_id] = os.path.basename(filename)
				msg.headers["message-id"] = msg_id
				msg.body = "Message-Id: "+msg_id+"\n"+msg.body
			else:
				# link to existing id
				msg_id = msg.headers["message-id"]
				self.body_db[msg_id] = os.path.basename(filename)
				# if we also have headers then we are set
				if self.header_db.has_key(msg_id):
					continue
			# no headers. we must make them
			msg.date_received = int(time.time())
			self.save_article(msg)
			print "MHBOX: Building headers for ", msg_id
		# kill old headers
		head_ids = self.header_db.keys()
		body_ids = self.body_db.keys()
		for id in head_ids:
			if not (id in body_ids):
				print "MHBOX: Nuking ", id," header"
				del self.header_db[id]

	def get_contents(self):
		return self.header_db.keys()

	def nuke(self):
		"""
		Delete database entirely.

		We don't delete the contents of the mhbox folder,
		since this probably isn't the desired behaviour.
		"""
		self.header_db.close()
		os.remove(self.prefix+".mhh")

	def __del__(self):
		# shutdown time :-)
		self.header_db.close()

	def load_header(self, msg_id):
		"""
		See pynebox.py
		"""
		try:
			return marshal.loads(self.header_db[msg_id])
		except (TypeError, KeyError):
			return None

	def _load_article(self, msg_id):
		"""
		Return body for message 'msg_id'
		"""
		header = self.load_header(msg_id)
		try:
			body_filename = self.body_db[msg_id]
			f = open(self.mhbox_path+"/"+body_filename, "r")
			body = f.read()
			f.close()
		except IOError:
			return (header, None)
		else:
			# convert body from strange mhbox format
			end_head = string.find(body, "\n--------\n")
			body = body[:end_head] + "\n\n" + body[end_head+10:]
			return (header, body)

	def _save_article(self, headers, body_text, msg_id):
		"""
		Do not use directly. use save_article.
		"""
		self.header_db[msg_id] = marshal.dumps(headers)
		##
		end_head = string.find(body_text, "\n\n")
		body = body_text[:end_head] + "\n--------\n" + body_text[end_head+2:]
		try:
			body_filename = self.body_db[msg_id]
		except KeyError:
			# No file exists for this. make one
			files = glob.glob(self.mhbox_path+"/*")
			sfiles = []
			for x in files:
				sfiles.append(os.path.basename(x))
			# find unique number
			i = 1
			while str(i) in sfiles:
				i = i + 1
			# record message-id to file number link
			self.body_db[msg_id] = str(i)
			body_filename = str(i)
	
		f = open(self.mhbox_path+"/"+body_filename, "w")
		f.write(body)
		f.close()

	def delete_article(self, msg_id):
		try:
			body_filename = self.body_db[msg_id]
			os.remove(self.mhbox_path+"/"+body_filename)
		except (KeyError, OSError):
			pass
		try:
			del self.header_db[msg_id]
		except KeyError:
			pass
		try:
			del self.body_db[msg_id]
		except KeyError:
			pass

	def has_article(self, msg_id):
		"""
		Is the article cached?
		"""
		# Note: we check for presence of headers not bodies
		if self.header_db.has_key(msg_id):
			return 1
		else:
			return 0




syntax highlighted by Code2HTML, v. 0.9.1