713e9f39811ad52e2c8dcee6e687084cfb0599c0
[frenchie/icalparse.git] / icalparse.py
1 #!/usr/bin/python
2 #
3 # Copyright (c) 2010 James French <[email protected]>
4 #
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:
11 #
12 # The above copyright notice and this permission notice shall be included in
13 # all copies or substantial portions of the Software.
14 #
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
21 # THE SOFTWARE.
22
23 import sys
24 import urlparse
25 import os
26
27 class InvalidICS(Exception): pass
28 class notJoined(Exception): pass
29
30 def lineJoiner(oldcal):
31         '''Takes a string containing a calendar and returns an array of its lines'''
32
33         if list(oldcal) == oldcal:
34                 oldcal = '\r\n'.join(oldcal)
35
36         oldcal.replace('\r\n ', '')
37         return oldcal.split('\r\n')
38
39
40 def lineFolder(oldcal, length=75):
41         '''Folds content lines to a specified length, returns a list'''
42
43         if length > 75:
44                 sys.stderr.write('WARN: lines > 75 octets are not RFC compliant\n')
45
46         cal = []
47         sl = length - 1
48
49         for line in oldcal:
50                 # Line fits inside length, do nothing
51                 if len(line.rstrip()) <= length:
52                         cal.append(line)
53                 else:
54                         brokenline = [line[0:length] + '\r\n']
55                         ll = length
56                         while ll < len(line.rstrip('\r\n')) + 1:
57                                 brokenline.append(' ' + line[ll:sl+ll].rstrip('\r\n') + '\r\n')
58                                 ll += sl
59                         cal += brokenline
60
61         return cal
62
63 def getContent(url='',stdin=False):
64         '''Generic content retriever, DO NOT use this function in a CGI script as
65         it can read from the local disk (which you probably don't want it to).
66         '''
67
68         # Special case, if this is a HTTP url, return the data from it using
69         # the HTTP functions which attempt to play a bit nicer.
70         parsedURL = urlparse.urlparse(url)
71         if 'http' in parsedURL[0]: return getHTTPContent(url)
72
73         if stdin:
74                 content = sys.stdin.read()
75                 return content
76
77         if not parsedURL[0]:
78                 try: content = open(os.path.abspath(url),'r').read()
79                 except (IOError, OSError), e:
80                         sys.stderr.write('%s\n'%e)
81                         sys.exit(1)
82                 return content
83
84         # If we've survived, use python's generic URL opening library to handle it
85         import urllib2
86         try:
87                 res = urllib2.urlopen(url)
88                 content = res.read()
89                 res.close()
90         except (urllib2.URLError, ValueError), e:
91                 sys.stderr.write('%s\n'%e)
92                 sys.exit(1)
93         return content
94
95
96 def getHTTPContent(url='',cache='.httplib2-cache'):
97         '''This function attempts to play nice when retrieving content from HTTP
98         services. It's what you should use in a CGI script. It will (by default)
99         slurp the first 20 bytes of the file and check that we are indeed looking
100         at an ICS file before going for broke.'''
101
102         try:
103                 import httplib2
104         except ImportError:
105                 import urllib2
106
107         if not url: return ''
108
109         if 'httplib2' in sys.modules:
110                 try: h = httplib2.Http('.httplib2-cache')
111                 except OSError: h = httplib2.Http()
112         else: h = False
113
114         try:
115                 if h: content = h.request(url)[1]
116                 return content
117         except ValueError, e:
118                 sys.stderr.write('%s\n'%e)
119                 sys.exit(1)
120
121         try:
122                 content = urllib2.urlopen(url).read()
123                 return content
124         except urllib2.URLError, e:
125                 sys.stderr.write('%s\n'%e)
126                 sys.exit(1)
127
128         return ''
129
130 if __name__ == '__main__':
131         from optparse import OptionParser
132         # If the user passed us a 'stdin' argument, we'll go with that,
133         # otherwise we'll try for a url opener
134
135         parser = OptionParser('usage: %prog [options] url')
136         parser.add_option('-s', '--stdin', action='store_true', dest='stdin',
137                 default=False, help='Take a calendar from standard input')
138         parser.add_option('-o', '--output', dest='outfile', default='',
139                 help='Specify output file (defaults to standard output)')
140
141         (options, args) = parser.parse_args()
142
143         if not args and not options.stdin:
144                 parser.print_usage()
145                 sys.exit(0)
146         elif not options.stdin:
147                 url = args[0]
148         else:
149                 url = ''
150
151         content = getContent(url, options.stdin)

UCC git Repository :: git.ucc.asn.au