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