mhlib.py revision 560131328c9ddfa25555048b1bf4d760c02e1dd7
1560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum# MH interface -- purely object-oriented (well, almost)
2560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum#
3560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum# Executive summary:
4560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum#
5560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum# import mhlib
6560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum#
7560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum# mh = mhlib.MH()         # use default mailbox directory and profile
8560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum# mh = mhlib.MH(mailbox)  # override mailbox location (default from profile)
9560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum# mh = mhlib.MH(mailbox, profile) # override mailbox and profile
10560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum#
11560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum# mh.error(format, ...)   # print error message -- can be overridden
12560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum# s = mh.getprofile(key)  # profile entry (None if not set)
13560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum# path = mh.getpath()     # mailbox pathname
14560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum# name = mh.getcontext()  # name of current folder
15560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum#
16560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum# list = mh.listfolders() # names of top-level folders
17560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum# list = mh.listallfolders() # names of all folders, including subfolders
18560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum# list = mh.listsubfolders(name) # direct subfolders of given folder
19560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum# list = mh.listallsubfolders(name) # all subfolders of given folder
20560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum#
21560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum# mh.makefolder(name)     # create new folder
22560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum# mh.deletefolder(name)   # delete folder -- must have no subfolders
23560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum#
24560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum# f = mh.openfolder(name) # new open folder object
25560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum#
26560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum# f.error(format, ...)    # same as mh.error(format, ...)
27560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum# path = f.getfullname()  # folder's full pathname
28560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum# path = f.getsequencesfilename() # full pathname of folder's sequences file
29560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum# path = f.getmessagefilename(n)  # full pathname of message n in folder
30560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum#
31560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum# list = f.listmessages() # list of messages in folder (as numbers)
32560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum# n = f.getcurrent()      # get current message
33560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum# f.setcurrent(n)         # set current message
34560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum#
35560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum# dict = f.getsequences() # dictionary of sequences in folder {name: list}
36560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum# f.putsequences(dict)    # write sequences back to folder
37560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum#
38560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum# m = f.openmessage(n)    # new open message object (costs a file descriptor)
39560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum# s = m.getheadertext()   # text of message's headers
40560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum# s = m.getheadertext(pred) # text of message's headers, filtered by pred
41560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum# s = m.getbodytext()     # text of message's body, decoded
42560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum# s = m.getbodytext(0)    # text of message's body, not decoded
43560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum#
44560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum# XXX To do, functionality:
45560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum# - remove, refile messages
46560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum# - annotate messages
47560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum# - create, send messages
48560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum#
49560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum# XXX To do, orgaanization:
50560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum# - move IntSet to separate file
51560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum# - move most Message functionality to module mimetools
52560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum
53560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum
54560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum# Customizable defaults
55560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum
56560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van RossumMH_PROFILE = '~/.mh_profile'
57560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van RossumPATH = '~/Mail'
58560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van RossumMH_SEQUENCES = '.mh_sequences'
59560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van RossumFOLDER_PROTECT = 0700
60560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum
61560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum
62560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum# Imported modules
63560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum
64560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossumimport os
65560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossumfrom stat import ST_NLINK
66560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossumimport regex
67560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossumimport string
68560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossumimport mimetools
69560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossumimport multifile
70560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum
71560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum
72560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum# Exported constants
73560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum
74560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van RossumError = 'mhlib.Error'
75560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum
76560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum
77560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum# Class representing a particular collection of folders.
78560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum# Optional constructor arguments are the pathname for the directory
79560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum# containing the collection, and the MH profile to use.
80560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum# If either is omitted or empty a default is used; the default
81560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum# directory is taken from the MH profile if it is specified there.
82560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum
83560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossumclass MH:
84560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum
85560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum	# Constructor
86560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum	def __init__(self, path = None, profile = None):
87560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		if not profile: profile = MH_PROFILE
88560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		self.profile = os.path.expanduser(profile)
89560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		if not path: path = self.getprofile('Path')
90560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		if not path: path = PATH
91560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		if not os.path.isabs(path) and path[0] != '~':
92560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum			path = os.path.join('~', path)
93560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		path = os.path.expanduser(path)
94560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		if not os.path.isdir(path): raise Error, 'MH() path not found'
95560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		self.path = path
96560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum
97560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum	# String representation
98560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum	def __repr__(self):
99560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		return 'MH(%s, %s)' % (`self.path`, `self.profile`)
100560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum
101560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum	# Routine to print an error.  May be overridden by a derived class
102560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum	def error(self, msg, *args):
103560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		sys.stderr.write('MH error: %\n' % (msg % args))
104560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum
105560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum	# Return a profile entry, None if not found
106560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum	def getprofile(self, key):
107560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		return pickline(self.profile, key)
108560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum
109560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum	# Return the path (the name of the collection's directory)
110560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum	def getpath(self):
111560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		return self.path
112560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum
113560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum	# Return the name of the current folder
114560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum	def getcontext(self):
115560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		context = pickline(os.path.join(self.getpath(), 'context'),
116560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum			  'Current-Folder')
117560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		if not context: context = 'inbox'
118560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		return context
119560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum
120560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum	# Return the names of the top-level folders
121560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum	def listfolders(self):
122560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		folders = []
123560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		path = self.getpath()
124560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		for name in os.listdir(path):
125560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum			if name in (os.curdir, os.pardir): continue
126560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum			fullname = os.path.join(path, name)
127560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum			if os.path.isdir(fullname):
128560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum				folders.append(name)
129560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		folders.sort()
130560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		return folders
131560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum
132560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum	# Return the names of the subfolders in a given folder
133560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum	# (prefixed with the given folder name)
134560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum	def listsubfolders(self, name):
135560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		fullname = os.path.join(self.path, name)
136560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		# Get the link count so we can avoid listing folders
137560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		# that have no subfolders.
138560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		st = os.stat(fullname)
139560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		nlinks = st[ST_NLINK]
140560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		if nlinks <= 2:
141560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum			return []
142560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		subfolders = []
143560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		subnames = os.listdir(fullname)
144560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		for subname in subnames:
145560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum			if subname in (os.curdir, os.pardir): continue
146560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum			fullsubname = os.path.join(fullname, subname)
147560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum			if os.path.isdir(fullsubname):
148560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum				name_subname = os.path.join(name, subname)
149560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum				subfolders.append(name_subname)
150560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum				# Stop looking for subfolders when
151560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum				# we've seen them all
152560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum				nlinks = nlinks - 1
153560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum				if nlinks <= 2:
154560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum					break
155560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		subfolders.sort()
156560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		return subfolders
157560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum
158560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum	# Return the names of all folders, including subfolders, recursively
159560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum	def listallfolders(self):
160560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		return self.listallsubfolders('')
161560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum
162560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum	# Return the names of subfolders in a given folder, recursively
163560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum	def listallsubfolders(self, name):
164560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		fullname = os.path.join(self.path, name)
165560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		# Get the link count so we can avoid listing folders
166560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		# that have no subfolders.
167560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		st = os.stat(fullname)
168560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		nlinks = st[ST_NLINK]
169560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		if nlinks <= 2:
170560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum			return []
171560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		subfolders = []
172560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		subnames = os.listdir(fullname)
173560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		for subname in subnames:
174560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum			if subname in (os.curdir, os.pardir): continue
175560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum			if subname[0] == ',' or isnumeric(subname): continue
176560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum			fullsubname = os.path.join(fullname, subname)
177560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum			if os.path.isdir(fullsubname):
178560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum				name_subname = os.path.join(name, subname)
179560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum				subfolders.append(name_subname)
180560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum				if not os.path.islink(fullsubname):
181560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum					subsubfolders = self.listallsubfolders(
182560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum						  name_subname)
183560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum					subfolders = subfolders + subsubfolders
184560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum				# Stop looking for subfolders when
185560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum				# we've seen them all
186560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum				nlinks = nlinks - 1
187560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum				if nlinks <= 2:
188560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum					break
189560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		subfolders.sort()
190560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		return subfolders
191560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum
192560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum	# Return a new Folder object for the named folder
193560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum	def openfolder(self, name):
194560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		return Folder(self, name)
195560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum
196560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum	# Create a new folder.  This raises os.error if the folder
197560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum	# cannot be created
198560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum	def makefolder(self, name):
199560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		protect = pickline(self.profile, 'Folder-Protect')
200560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		if protect and isnumeric(protect):
201560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum			mode = eval('0' + protect)
202560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		else:
203560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum			mode = FOLDER_PROTECT
204560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		os.mkdir(os.path.join(self.getpath(), name), mode)
205560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum
206560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum	# Delete a folder.  This removes files in the folder but not
207560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum	# subdirectories.  If deleting the folder itself fails it
208560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum	# raises os.error
209560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum	def deletefolder(self, name):
210560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		fullname = os.path.join(self.getpath(), name)
211560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		for subname in os.listdir(fullname):
212560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum			if subname in (os.curdir, os.pardir): continue
213560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum			fullsubname = os.path.join(fullname, subname)
214560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum			try:
215560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum				os.unlink(fullsubname)
216560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum			except os.error:
217560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum				self.error('%s not deleted, continuing...' %
218560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum					  fullsubname)
219560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		os.rmdir(fullname)
220560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum
221560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum
222560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum# Class representing a particular folder
223560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum
224560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossumnumericprog = regex.compile('[1-9][0-9]*')
225560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossumdef isnumeric(str):
226560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum	return numericprog.match(str) == len(str)
227560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum
228560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossumclass Folder:
229560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum
230560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum	# Constructor
231560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum	def __init__(self, mh, name):
232560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		self.mh = mh
233560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		self.name = name
234560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		if not os.path.isdir(self.getfullname()):
235560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum			raise Error, 'no folder %s' % name
236560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum
237560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum	# String representation
238560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum	def __repr__(self):
239560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		return 'Folder(%s, %s)' % (`self.mh`, `self.name`)
240560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum
241560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum	# Error message handler
242560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum	def error(self, *args):
243560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		apply(self.mh.error, args)
244560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum
245560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum	# Return the full pathname of the folder
246560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum	def getfullname(self):
247560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		return os.path.join(self.mh.path, self.name)
248560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum
249560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum	# Return the full pathname of the folder's sequences file
250560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum	def getsequencesfilename(self):
251560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		return os.path.join(self.getfullname(), MH_SEQUENCES)
252560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum
253560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum	# Return the full pathname of a message in the folder
254560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum	def getmessagefilename(self, n):
255560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		return os.path.join(self.getfullname(), str(n))
256560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum
257560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum	# Return list of direct subfolders
258560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum	def listsubfolders(self):
259560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		return self.mh.listsubfolders(self.name)
260560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum
261560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum	# Return list of all subfolders
262560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum	def listallsubfolders(self):
263560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		return self.mh.listallsubfolders(self.name)
264560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum
265560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum	# Return the list of messages currently present in the folder.
266560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum	# As a side effect, set self.last to the last message (or 0)
267560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum	def listmessages(self):
268560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		messages = []
269560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		for name in os.listdir(self.getfullname()):
270560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum			if isnumeric(name):
271560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum				messages.append(eval(name))
272560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		messages.sort()
273560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		if messages:
274560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum			self.last = max(messages)
275560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		else:
276560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum			self.last = 0
277560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		return messages
278560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum
279560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum	# Return the set of sequences for the folder
280560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum	def getsequences(self):
281560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		sequences = {}
282560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		fullname = self.getsequencesfilename()
283560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		try:
284560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum			f = open(fullname, 'r')
285560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		except IOError:
286560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum			return sequences
287560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		while 1:
288560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum			line = f.readline()
289560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum			if not line: break
290560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum			fields = string.splitfields(line, ':')
291560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum			if len(fields) <> 2:
292560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum				self.error('bad sequence in %s: %s' %
293560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum					  (fullname, string.strip(line)))
294560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum			key = string.strip(fields[0])
295560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum			value = IntSet(string.strip(fields[1]), ' ').tolist()
296560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum			sequences[key] = value
297560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		return sequences
298560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum
299560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum	# Write the set of sequences back to the folder
300560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum	def putsequences(self, sequences):
301560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		fullname = self.getsequencesfilename()
302560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		f = open(fullname, 'w')
303560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		for key in sequences.keys():
304560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum			s = IntSet('', ' ')
305560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum			s.fromlist(sequences[key])
306560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum			f.write('%s: %s\n' % (key, s.tostring()))
307560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum
308560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum	# Return the current message.  Raise KeyError when there is none
309560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum	def getcurrent(self):
310560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		return min(self.getsequences()['cur'])
311560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum
312560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum	# Set the current message
313560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum	def setcurrent(self, n):
314560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		updateline(self.getsequencesfilename(), 'cur', str(n), 0)
315560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum
316560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum	# Open a message -- returns a mimetools.Message object
317560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum	def openmessage(self, n):
318560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		path = self.getmessagefilename(n)
319560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		return Message(self, n)
320560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum
321560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum	# Remove one or more messages -- may raise os.error
322560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum	def removemessages(self, list):
323560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		errors = []
324560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		deleted = []
325560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		for n in list:
326560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum			path = self.getmessagefilename(n)
327560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum			commapath = self.getmessagefilename(',' + str(n))
328560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum			try:
329560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum				os.unlink(commapath)
330560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum			except os.error:
331560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum				pass
332560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum			try:
333560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum				os.rename(path, commapath)
334560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum			except os.error, msg:
335560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum				errors.append(msg)
336560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum			else:
337560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum				deleted.append(n)
338560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		if deleted:
339560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum			self.removefromallsequences(deleted)
340560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		if errors:
341560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum			if len(errors) == 1:
342560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum				raise os.error, errors[0]
343560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum			else:
344560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum				raise os.error, ('multiple errors:', errors)
345560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum
346560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum	# Refile one or more messages -- may raise os.error.
347560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum	# 'tofolder' is an open folder object
348560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum	def refilemessages(self, list, tofolder):
349560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		errors = []
350560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		refiled = []
351560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		for n in list:
352560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum			ton = tofolder.getlast() + 1
353560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum			path = self.getmessagefilename(n)
354560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum			topath = tofolder.getmessagefilename(ton)
355560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum			try:
356560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum				os.rename(path, topath)
357560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum				# XXX What if it's on a different filesystem?
358560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum			except os.error, msg:
359560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum				errors.append(msg)
360560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum			else:
361560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum				tofolder.setlast(ton)
362560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum				refiled.append(n)
363560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		if refiled:
364560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum			self.removefromallsequences(refiled)
365560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		if errors:
366560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum			if len(errors) == 1:
367560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum				raise os.error, errors[0]
368560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum			else:
369560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum				raise os.error, ('multiple errors:', errors)
370560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum
371560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum	# Remove one or more messages from all sequeuces (including last)
372560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum	def removefromallsequences(self, list):
373560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		if hasattr(self, 'last') and self.last in list:
374560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum			del self.last
375560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		sequences = self.getsequences()
376560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		changed = 0
377560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		for name, seq in sequences.items():
378560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum			for n in list:
379560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum				if n in seq:
380560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum					seq.remove(n)
381560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum					changed = 1
382560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum					if not seq:
383560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum						del sequences[name]
384560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		if changed:
385560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum			self.putsequences()
386560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum
387560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum	# Return the last message number
388560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum	def getlast(self):
389560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		if not hasattr(self, 'last'):
390560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum			messages = self.listmessages()
391560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		return self.last
392560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum
393560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum	# Set the last message number
394560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum	def setlast(self, last):
395560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		if last is None:
396560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum			if hasattr(self, 'last'):
397560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum				del self.last
398560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		else:
399560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum			self.last = last
400560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum
401560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossumclass Message(mimetools.Message):
402560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum
403560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum	# Constructor
404560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum	def __init__(self, f, n, fp = None):
405560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		self.folder = f
406560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		self.number = n
407560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		if not fp:
408560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum			path = f.getmessagefilename(n)
409560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum			fp = open(path, 'r')
410560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		mimetools.Message.__init__(self, fp)
411560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum
412560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum	# String representation
413560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum	def __repr__(self):
414560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		return 'Message(%s, %s)' % (repr(self.folder), self.number)
415560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum
416560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum	# Return the message's header text as a string.  If an
417560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum	# argument is specified, it is used as a filter predicate to
418560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum	# decide which headers to return (its argument is the header
419560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum	# name converted to lower case).
420560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum	def getheadertext(self, pred = None):
421560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		if not pred:
422560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum			return string.joinfields(self.headers, '')
423560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		headers = []
424560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		hit = 0
425560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		for line in self.headers:
426560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum			if line[0] not in string.whitespace:
427560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum				i = string.find(line, ':')
428560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum				if i > 0:
429560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum					hit = pred(string.lower(line[:i]))
430560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum			if hit: headers.append(line)
431560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		return string.joinfields(headers, '')
432560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum
433560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum	# Return the message's body text as string.  This undoes a
434560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum	# Content-Transfer-Encoding, but does not interpret other MIME
435560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum	# features (e.g. multipart messages).  To suppress to
436560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum	# decoding, pass a 0 as argument
437560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum	def getbodytext(self, decode = 1):
438560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		self.fp.seek(self.startofbody)
439560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		encoding = self.getencoding()
440560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		if not decode or encoding in ('7bit', '8bit', 'binary'):
441560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum			return self.fp.read()
442560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		from StringIO import StringIO
443560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		output = StringIO()
444560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		mimetools.decode(self.fp, output, encoding)
445560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		return output.getvalue()
446560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum
447560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum	# Only for multipart messages: return the message's body as a
448560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum	# list of SubMessage objects.  Each submessage object behaves
449560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum	# (almost) as a Message object.
450560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum	def getbodyparts(self):
451560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		if self.getmaintype() != 'multipart':
452560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum			raise Error, \
453560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum			      'Content-Type is not multipart/*'
454560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		bdry = self.getparam('boundary')
455560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		if not bdry:
456560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum			raise Error, 'multipart/* without boundary param'
457560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		self.fp.seek(self.startofbody)
458560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		mf = multifile.MultiFile(self.fp)
459560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		mf.push(bdry)
460560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		parts = []
461560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		while mf.next():
462560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum			n = str(self.number) + '.' + `1 + len(parts)`
463560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum			part = SubMessage(self.folder, n, mf)
464560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum			parts.append(part)
465560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		mf.pop()
466560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		return parts
467560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum
468560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum	# Return body, either a string or a list of messages
469560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum	def getbody(self):
470560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		if self.getmaintype() == 'multipart':
471560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum			return self.getbodyparts()
472560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		else:
473560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum			return self.getbodytext()
474560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum
475560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum
476560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossumclass SubMessage(Message):
477560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum
478560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum	# Constructor
479560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum	def __init__(self, f, n, fp):
480560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		Message.__init__(self, f, n, fp)
481560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		if self.getmaintype() == 'multipart':
482560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum			self.body = Message.getbodyparts(self)
483560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		else:
484560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum			self.body = Message.getbodytext(self)
485560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum			# XXX If this is big, should remember file pointers
486560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum
487560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum	# String representation
488560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum	def __repr__(self):
489560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		f, n, fp = self.folder, self.number, self.fp
490560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		return 'SubMessage(%s, %s, %s)' % (f, n, fp)
491560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum
492560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum	def getbodytext(self):
493560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		if type(self.body) == type(''):
494560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum			return self.body
495560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum
496560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum	def getbodyparts(self):
497560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		if type(self.body) == type([]):
498560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum			return self.body
499560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum
500560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum	def getbody(self):
501560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		return self.body
502560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum
503560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum
504560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum# Class implementing sets of integers.
505560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum#
506560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum# This is an efficient representation for sets consisting of several
507560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum# continuous ranges, e.g. 1-100,200-400,402-1000 is represented
508560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum# internally as a list of three pairs: [(1,100), (200,400),
509560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum# (402,1000)].  The internal representation is always kept normalized.
510560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum#
511560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum# The constructor has up to three arguments:
512560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum# - the string used to initialize the set (default ''),
513560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum# - the separator between ranges (default ',')
514560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum# - the separator between begin and end of a range (default '-')
515560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum# The separators may be regular expressions and should be different.
516560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum#
517560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum# The tostring() function yields a string that can be passed to another
518560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum# IntSet constructor; __repr__() is a valid IntSet constructor itself.
519560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum#
520560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum# XXX The default begin/end separator means that negative numbers are
521560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum#     not supported very well.
522560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum#
523560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum# XXX There are currently no operations to remove set elements.
524560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum
525560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossumclass IntSet:
526560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum
527560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum	def __init__(self, data = None, sep = ',', rng = '-'):
528560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		self.pairs = []
529560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		self.sep = sep
530560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		self.rng = rng
531560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		if data: self.fromstring(data)
532560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum
533560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum	def reset(self):
534560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		self.pairs = []
535560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum
536560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum	def __cmp__(self, other):
537560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		return cmp(self.pairs, other.pairs)
538560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum
539560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum	def __hash__(self):
540560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		return hash(self.pairs)
541560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum
542560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum	def __repr__(self):
543560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		return 'IntSet(%s, %s, %s)' % (`self.tostring()`,
544560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum			  `self.sep`, `self.rng`)
545560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum
546560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum	def normalize(self):
547560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		self.pairs.sort()
548560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		i = 1
549560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		while i < len(self.pairs):
550560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum			alo, ahi = self.pairs[i-1]
551560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum			blo, bhi = self.pairs[i]
552560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum			if ahi >= blo-1:
553560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum				self.pairs[i-1:i+1] = [
554560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum				      (alo, max(ahi, bhi))]
555560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum			else:
556560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum				i = i+1
557560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum
558560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum	def tostring(self):
559560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		s = ''
560560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		for lo, hi in self.pairs:
561560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum			if lo == hi: t = `lo`
562560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum			else: t = `lo` + self.rng + `hi`
563560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum			if s: s = s + (self.sep + t)
564560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum			else: s = t
565560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		return s
566560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum
567560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum	def tolist(self):
568560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		l = []
569560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		for lo, hi in self.pairs:
570560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum			m = range(lo, hi+1)
571560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum			l = l + m
572560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		return l
573560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum
574560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum	def fromlist(self, list):
575560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		for i in list:
576560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum			self.append(i)
577560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum
578560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum	def clone(self):
579560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		new = IntSet()
580560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		new.pairs = self.pairs[:]
581560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		return new
582560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum
583560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum	def min(self):
584560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		return self.pairs[0][0]
585560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum
586560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum	def max(self):
587560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		return self.pairs[-1][-1]
588560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum
589560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum	def contains(self, x):
590560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		for lo, hi in self.pairs:
591560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum			if lo <= x <= hi: return 1
592560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		return 0
593560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum
594560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum	def append(self, x):
595560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		for i in range(len(self.pairs)):
596560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum			lo, hi = self.pairs[i]
597560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum			if x < lo: # Need to insert before
598560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum				if x+1 == lo:
599560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum					self.pairs[i] = (x, hi)
600560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum				else:
601560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum					self.pairs.insert(i, (x, x))
602560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum				if i > 0 and x-1 == self.pairs[i-1][1]:
603560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum					# Merge with previous
604560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum					self.pairs[i-1:i+1] = [
605560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum					        (self.pairs[i-1][0],
606560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum					         self.pairs[i][1])
607560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum					      ]
608560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum				return
609560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum			if x <= hi: # Already in set
610560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum				return
611560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		i = len(self.pairs) - 1
612560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		if i >= 0:
613560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum			lo, hi = self.pairs[i]
614560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum			if x-1 == hi:
615560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum				self.pairs[i] = lo, x
616560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum				return
617560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		self.pairs.append((x, x))
618560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum
619560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum	def addpair(self, xlo, xhi):
620560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		if xlo > xhi: return
621560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		self.pairs.append((xlo, xhi))
622560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		self.normalize()
623560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum
624560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum	def fromstring(self, data):
625560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		import string, regsub
626560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		new = []
627560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		for part in regsub.split(data, self.sep):
628560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum			list = []
629560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum			for subp in regsub.split(part, self.rng):
630560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum				s = string.strip(subp)
631560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum				list.append(string.atoi(s))
632560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum			if len(list) == 1:
633560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum				new.append((list[0], list[0]))
634560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum			elif len(list) == 2 and list[0] <= list[1]:
635560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum				new.append((list[0], list[1]))
636560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum			else:
637560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum				raise ValueError, 'bad data passed to IntSet'
638560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		self.pairs = self.pairs + new
639560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		self.normalize()
640560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum
641560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum
642560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum# Subroutines to read/write entries in .mh_profile and .mh_sequences
643560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum
644560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossumdef pickline(file, key, casefold = 1):
645560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum	try:
646560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		f = open(file, 'r')
647560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum	except IOError:
648560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		return None
649560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum	pat = key + ':'
650560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum	if casefold:
651560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		prog = regex.compile(pat, regex.casefold)
652560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum	else:
653560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		prog = regex.compile(pat)
654560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum	while 1:
655560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		line = f.readline()
656560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		if not line: break
657560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		if prog.match(line) == len(line):
658560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum			text = line[len(key)+1:]
659560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum			while 1:
660560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum				line = f.readline()
661560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum				if not line or \
662560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum				   line[0] not in string.whitespace:
663560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum					break
664560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum				text = text + line
665560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum			return string.strip(text)
666560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum	return None
667560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum
668560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossumdef updateline(file, key, value, casefold = 1):
669560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum	try:
670560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		f = open(file, 'r')
671560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		lines = f.readlines()
672560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		f.close()
673560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum	except IOError:
674560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		lines = []
675560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum	pat = key + ':\(.*\)\n'
676560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum	if casefold:
677560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		prog = regex.compile(pat, regex.casefold)
678560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum	else:
679560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		prog = regex.compile(pat)
680560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum	if value is None:
681560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		newline = None
682560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum	else:
683560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		newline = '%s: %s' % (key, value)
684560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum	for i in range(len(lines)):
685560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		line = lines[i]
686560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		if prog.match(line) == len(line):
687560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum			if newline is None:
688560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum				del lines[i]
689560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum			else:
690560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum				lines[i] = newline
691560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum			break
692560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum	else:
693560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		if newline is not None:
694560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum			lines.append(newline)
695560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum	f = open(tempfile, 'w')
696560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum	for line in lines:
697560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		f.write(line)
698560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum	f.close()
699560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum
700560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum
701560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum# Test program
702560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum
703560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossumdef test():
704560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum	global mh, f
705560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum	os.system('rm -rf $HOME/Mail/@test')
706560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum	mh = MH()
707560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum	def do(s): print s; print eval(s)
708560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum	do('mh.listfolders()')
709560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum	do('mh.listallfolders()')
710560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum	testfolders = ['@test', '@test/test1', '@test/test2',
711560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		       '@test/test1/test11', '@test/test1/test12',
712560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum		       '@test/test1/test11/test111']
713560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum	for t in testfolders: do('mh.makefolder(%s)' % `t`)
714560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum	do('mh.listsubfolders(\'@test\')')
715560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum	do('mh.listallsubfolders(\'@test\')')
716560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum	f = mh.openfolder('@test')
717560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum	do('f.listsubfolders()')
718560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum	do('f.listallsubfolders()')
719560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum	do('f.getsequences()')
720560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum	seqs = f.getsequences()
721560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum	seqs['foo'] = IntSet('1-10 12-20', ' ').tolist()
722560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum	print seqs
723560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum	f.putsequences(seqs)
724560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum	do('f.getsequences()')
725560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum	testfolders.reverse()
726560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum	for t in testfolders: do('mh.deletefolder(%s)' % `t`)
727560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum	do('mh.getcontext()')
728560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum	context = mh.getcontext()
729560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum	f = mh.openfolder(context)
730560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum	do('f.listmessages()')
731560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum	do('f.getcurrent()')
732560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum
733560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum
734560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossumif __name__ == '__main__':
735560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum	test()
736