1 #!/usr/bin/env python 2 3 "A calendar application." 4 5 import WebStack.Generic 6 import time 7 import os 8 9 class CalendarResource: 10 11 """ 12 A resource which handles incoming calendars and viewing requests. 13 An arbitrary set of rules can be applied to determine what is to be done 14 with a request, and in this case, the application appears as a directory of 15 calendars, yet also accepts incoming calendars. 16 """ 17 18 resource_dir = os.path.join(os.path.split(__file__)[0], "calendars") 19 urlencoding = "utf-8" 20 21 def __init__(self): 22 if not os.path.exists(self.resource_dir): 23 os.mkdir(self.resource_dir) 24 25 def respond(self, trans): 26 27 """ 28 Examine the incoming request, either saving a calendar or displaying 29 one. 30 """ 31 32 # Remember uploaded calendars using a session. 33 34 session = trans.get_session(create=1) 35 36 # Determine the action to be taken. 37 38 method = trans.get_request_method() 39 40 # NOTE: Some frameworks do not pass in the content type. 41 # NOTE: We always assume that calendar files are being uploaded. 42 43 content_type = trans.get_content_type() 44 calendar_name = trans.get_virtual_path_info(self.urlencoding).split("/")[-1] 45 46 # Handle uploads. 47 48 if method == "PUT": 49 50 # Get the last path component as the name of the calendar. 51 # NOTE: This could be improved to permit hierarchical naming. 52 53 input = trans.get_request_stream() 54 data = input.read() 55 56 # Store the calendar in the directory. 57 58 f = open(os.path.join(self.resource_dir, calendar_name), "wb") 59 f.write(data) 60 f.close() 61 62 # Handle directory browsing. 63 64 elif method == "PROPFIND": 65 trans.set_response_code(207) 66 trans.set_content_type(WebStack.Generic.ContentType("text/html")) 67 out = trans.get_response_stream() 68 out.write("""<?xml version="1.0"?> 69 <D:multistatus xmlns:D="DAV:"> 70 """) 71 72 if trans.get_virtual_path_info(self.urlencoding) == "/": 73 time_now = time.strftime("%Y-%m-%dT%TZ", time.gmtime(time.time())) 74 out.write(""" 75 <D:response> 76 <D:href>%s</D:href> 77 <D:propstat> 78 <D:prop> 79 <D:creationdate>%s</D:creationdate> 80 <D:displayname>%s</D:displayname> 81 <D:resourcetype> 82 <D:collection/> 83 </D:resourcetype> 84 </D:prop> 85 <D:status>HTTP/1.1 200 OK</D:status> 86 </D:propstat> 87 </D:response> 88 """ % (trans.get_path_without_query(self.urlencoding), time_now, trans.get_path_without_query(self.urlencoding))) 89 90 for filename in os.listdir(self.resource_dir): 91 pathname = os.path.join(self.resource_dir, filename) 92 created = time.strftime("%Y-%m-%dT%TZ", time.gmtime(os.path.getctime(pathname))) 93 size = os.path.getsize(pathname) 94 out.write(""" 95 <D:response> 96 <D:href>%s%s</D:href> 97 <D:propstat> 98 <D:prop> 99 <D:creationdate>%s</D:creationdate> 100 <D:displayname>%s</D:displayname> 101 <D:resourcetype/> 102 <D:getcontenttype>%s</D:getcontenttype> 103 <D:getcontentlength>%s</D:getcontentlength> 104 </D:prop> 105 <D:status>HTTP/1.1 200 OK</D:status> 106 </D:propstat> 107 </D:response> 108 """ % (trans.get_path_without_query(self.urlencoding), filename, created, filename, "text/calendar", size)) 109 110 out.write(""" 111 </D:multistatus> 112 """) 113 114 # Handle downloads. 115 116 elif method == "GET": 117 trans.set_content_type(WebStack.Generic.ContentType("text/html")) 118 out = trans.get_response_stream() 119 f = open(os.path.join(self.resource_dir, calendar_name)) 120 out.write(f.read()) 121 f.close() 122 123 # Handle deletion. 124 125 elif method == "DELETE": 126 try: 127 os.remove(os.path.join(self.resource_dir, calendar_name)) 128 except OSError: 129 trans.set_response_code(500) 130 131 # Handle renaming. 132 133 elif method in ("MOVE", "COPY"): 134 destinations = trans.get_header_values("Destination") 135 if len(destinations) != 1: 136 trans.set_response_code(500) 137 else: 138 try: 139 # Convert the URL into a filename. 140 # NOTE: Assume that the URL references the same "directory". 141 142 destination = destinations[0].split("/")[-1] 143 144 if method == "MOVE": 145 os.rename(os.path.join(self.resource_dir, calendar_name), os.path.join(self.resource_dir, destination)) 146 elif method == "COPY": 147 f_old = open(os.path.join(self.resource_dir, calendar_name), "rb") 148 f_new = open(os.path.join(self.resource_dir, destination), "wb") 149 f_new.write(f_old.read()) 150 f_new.close() 151 f_old.close() 152 153 # NOTE: We do not observe the rules regarding overwriting 154 # NOTE: and the appropriate status codes. 155 156 trans.set_header_value("Location", destinations[0]) 157 trans.set_response_code(201) 158 159 except OSError: 160 trans.set_response_code(500) 161 162 # Disallow other methods. 163 164 else: 165 trans.set_response_code(405) 166 167 # vim: tabstop=4 expandtab shiftwidth=4