5 # Permission is hereby granted, free of charge, to any person obtaining a copy
6 # of this software and associated documentation files (the "Software"), to deal
7 # in the Software without restriction, including without limitation the rights
8 # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 # copies of the Software, and to permit persons to whom the Software is
10 # furnished to do so, subject to the following conditions:
12 # The above copyright notice and this permission notice shall be included in
13 # all copies or substantial portions of the Software.
15 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
28 class InvalidICS(Exception): pass
29 class notJoined(Exception): pass
30 class IncompleteICS(InvalidICS): pass
33 def lineJoiner(oldcal):
34 '''Takes a string containing a calendar and returns an array of its lines'''
36 if not oldcal[0:15] == 'BEGIN:VCALENDAR':
37 raise InvalidICS, "Does not appear to be a valid ICS file"
39 if not 'END:VCALENDAR' in oldcal[-15:-1]:
40 raise IncompleteICS, "File appears to be incomplete"
42 if list(oldcal) == oldcal:
43 oldcal = '\r\n'.join(oldcal)
45 oldcal = oldcal.replace('\r\n ', '').replace('\r\n\t','')
46 return oldcal.strip().split('\r\n')
49 def lineFolder(oldcal, length=75):
50 '''Folds content lines to a specified length, returns a list'''
53 sys.stderr.write('WARN: lines > 75 octets are not RFC compliant\n')
59 # Line fits inside length, do nothing
60 if len(line.rstrip()) <= length:
63 brokenline = [line[0:length] + '\r\n']
65 while ll < len(line.rstrip('\r\n')) + 1:
66 brokenline.append(' ' + line[ll:sl+ll].rstrip('\r\n') + '\r\n')
73 def getContent(url='',stdin=False):
74 '''Generic content retriever, DO NOT use this function in a CGI script as
75 it can read from the local disk (which you probably don't want it to).
78 # Special case, if this is a HTTP url, return the data from it using
79 # the HTTP functions which attempt to play a bit nicer.
80 parsedURL = urlparse.urlparse(url)
81 if 'http' in parsedURL[0]: return getHTTPContent(url)
84 content = sys.stdin.read()
88 try: content = open(os.path.abspath(url),'r').read()
89 except (IOError, OSError), e:
90 sys.stderr.write('%s\n'%e)
94 # If we've survived, use python's generic URL opening library to handle it
97 res = urllib2.urlopen(url)
100 except (urllib2.URLError, ValueError), e:
101 sys.stderr.write('%s\n'%e)
106 def getHTTPContent(url='',cache='.httplib2-cache'):
107 '''This function attempts to play nice when retrieving content from HTTP
108 services. It's what you should use in a CGI script. It will (by default)
109 slurp the first 20 bytes of the file and check that we are indeed looking
110 at an ICS file before going for broke.'''
117 if not url: return ''
119 if 'httplib2' in sys.modules:
120 try: h = httplib2.Http('.httplib2-cache')
121 except OSError: h = httplib2.Http()
125 if h: content = h.request(url)[1]
127 except ValueError, e:
128 sys.stderr.write('%s\n'%e)
132 content = urllib2.urlopen(url).read()
134 except urllib2.URLError, e:
135 sys.stderr.write('%s\n'%e)
140 if __name__ == '__main__':
141 from optparse import OptionParser
142 # If the user passed us a 'stdin' argument, we'll go with that,
143 # otherwise we'll try for a url opener
145 parser = OptionParser('usage: %prog [options] url')
146 parser.add_option('-s', '--stdin', action='store_true', dest='stdin',
147 default=False, help='Take a calendar from standard input')
148 parser.add_option('-o', '--output', dest='outfile', default='',
149 help='Specify output file (defaults to standard output)')
151 (options, args) = parser.parse_args()
153 if not args and not options.stdin:
156 elif not options.stdin:
161 content = getContent(url, options.stdin)
162 cal = lineJoiner(content)