]> code.delx.au - offlineimap/blob - offlineimap/imaputil.py
Update FSF address
[offlineimap] / offlineimap / imaputil.py
1 # IMAP utility module
2 # Copyright (C) 2002 John Goerzen
3 # <jgoerzen@complete.org>
4 #
5 # This program is free software; you can redistribute it and/or modify
6 # it under the terms of the GNU General Public License as published by
7 # the Free Software Foundation; either version 2 of the License, or
8 # (at your option) any later version.
9 #
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU General Public License for more details.
14 #
15 # You should have received a copy of the GNU General Public License
16 # along with this program; if not, write to the Free Software
17 # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18
19 import re, string, types
20 from offlineimap.ui import UIBase
21 quotere = re.compile('^("(?:[^"]|\\\\")*")')
22
23 def debug(*args):
24 msg = []
25 for arg in args:
26 msg.append(str(arg))
27 UIBase.getglobalui().debug('imap', " ".join(msg))
28
29 def dequote(string):
30 """Takes a string which may or may not be quoted and returns it, unquoted.
31 This function does NOT consider parenthised lists to be quoted.
32 """
33
34 debug("dequote() called with input:", string)
35 if not (string[0] == '"' and string[-1] == '"'):
36 return string
37 string = string[1:-1] # Strip off quotes.
38 string = string.replace('\\"', '"')
39 string = string.replace('\\\\', '\\')
40 debug("dequote() returning:", string)
41 return string
42
43 def flagsplit(string):
44 if string[0] != '(' or string[-1] != ')':
45 raise ValueError, "Passed string '%s' is not a flag list" % string
46 return imapsplit(string[1:-1])
47
48 def options2hash(list):
49 debug("options2hash called with input:", list)
50 retval = {}
51 counter = 0
52 while (counter < len(list)):
53 retval[list[counter]] = list[counter + 1]
54 counter += 2
55 debug("options2hash returning:", retval)
56 return retval
57
58 def flags2hash(string):
59 return options2hash(flagsplit(string))
60
61 def imapsplit(imapstring):
62 """Takes a string from an IMAP conversation and returns a list containing
63 its components. One example string is:
64
65 (\\HasNoChildren) "." "INBOX.Sent"
66
67 The result from parsing this will be:
68
69 ['(\\HasNoChildren)', '"."', '"INBOX.Sent"']"""
70
71 debug("imapsplit() called with input:", imapstring)
72 if type(imapstring) != types.StringType:
73 debug("imapsplit() got a non-string input; working around.")
74 # Sometimes, imaplib will throw us a tuple if the input
75 # contains a literal. See Python bug
76 # #619732 at https://sourceforge.net/tracker/index.php?func=detail&aid=619732&group_id=5470&atid=105470
77 # One example is:
78 # result[0] = '() "\\\\" Admin'
79 # result[1] = ('() "\\\\" {19}', 'Folder\\2')
80 #
81 # This function will effectively get result[0] or result[1], so
82 # if we get the result[1] version, we need to parse apart the tuple
83 # and figure out what to do with it. Each even-numbered
84 # part of it should end with the {} number, and each odd-numbered
85 # part should be directly a part of the result. We'll
86 # artificially quote it to help out.
87 retval = []
88 for i in range(len(imapstring)):
89 if i % 2: # Odd: quote then append.
90 arg = imapstring[i]
91 # Quote code lifted from imaplib
92 arg = arg.replace('\\', '\\\\')
93 arg = arg.replace('"', '\\"')
94 arg = '"%s"' % arg
95 debug("imapsplit() non-string [%d]: Appending %s" %\
96 (i, arg))
97 retval.append(arg)
98 else:
99 # Even -- we have a string that ends with a literal
100 # size specifier. We need to strip off that, then run
101 # what remains through the regular imapsplit parser.
102 # Recursion to the rescue.
103 arg = imapstring[i]
104 arg = re.sub('\{\d+\}$', '', arg)
105 debug("imapsplit() non-string [%d]: Feeding %s to recursion" %\
106 (i, arg))
107 retval.extend(imapsplit(arg))
108 debug("imapsplit() non-string: returning %s" % str(retval))
109 return retval
110
111 workstr = imapstring.strip()
112 retval = []
113 while len(workstr):
114 if workstr[0] == '(':
115 rparenc = 1 # count of right parenthesis to match
116 rpareni = 1 # position to examine
117 while rparenc: # Find the end of the group.
118 if workstr[rpareni] == ')': # end of a group
119 rparenc -= 1
120 elif workstr[rpareni] == '(': # start of a group
121 rparenc += 1
122 rpareni += 1 # Move to next character.
123 parenlist = workstr[0:rpareni]
124 workstr = workstr[rpareni:].lstrip()
125 retval.append(parenlist)
126 elif workstr[0] == '"':
127 quotelist = quotere.search(workstr).group(1)
128 workstr = workstr[len(quotelist):].lstrip()
129 retval.append(quotelist)
130 else:
131 splits = string.split(workstr, maxsplit = 1)
132 splitslen = len(splits)
133 # The unquoted word is splits[0]; the remainder is splits[1]
134 if splitslen == 2:
135 # There's an unquoted word, and more string follows.
136 retval.append(splits[0])
137 workstr = splits[1] # split will have already lstripped it
138 continue
139 elif splitslen == 1:
140 # We got a last unquoted word, but nothing else
141 retval.append(splits[0])
142 # Nothing remains. workstr would be ''
143 break
144 elif splitslen == 0:
145 # There was not even an unquoted word.
146 break
147 debug("imapsplit() returning:", retval)
148 return retval
149
150 def flagsimap2maildir(flagstring):
151 flagmap = {'\\seen': 'S',
152 '\\answered': 'R',
153 '\\flagged': 'F',
154 '\\deleted': 'T',
155 '\\draft': 'D'}
156 retval = []
157 imapflaglist = [x.lower() for x in flagstring[1:-1].split()]
158 for imapflag in imapflaglist:
159 if flagmap.has_key(imapflag):
160 retval.append(flagmap[imapflag])
161 retval.sort()
162 return retval
163
164 def flagsmaildir2imap(list):
165 flagmap = {'S': '\\Seen',
166 'R': '\\Answered',
167 'F': '\\Flagged',
168 'T': '\\Deleted',
169 'D': '\\Draft'}
170 retval = []
171 for mdflag in list:
172 if flagmap.has_key(mdflag):
173 retval.append(flagmap[mdflag])
174 retval.sort()
175 return '(' + ' '.join(retval) + ')'
176
177 def listjoin(list):
178 start = None
179 end = None
180 retval = []
181
182 def getlist(start, end):
183 if start == end:
184 return(str(start))
185 else:
186 return(str(start) + ":" + str(end))
187
188
189 for item in list:
190 if start == None:
191 # First item.
192 start = item
193 end = item
194 elif item == end + 1:
195 # An addition to the list.
196 end = item
197 else:
198 # Here on: starting a new list.
199 retval.append(getlist(start, end))
200 start = item
201 end = item
202
203 if start != None:
204 retval.append(getlist(start, end))
205
206 return ",".join(retval)
207
208
209
210
211