#
# 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