[Medusa-dev] Autoindex
SergioFernández
sfmunoz@teleline.es
Sun Dec 1 12:56:03 2002
Hello:
I've added autoindex support to Medusa Web Server. Autoindex shows you the
content of the directory you are looking for in a web server when there's
no index.html/default.html file in that directory.
Medusa returned a '404 Not Found' code when there were no
index.html/default.html file in the directory. I didn't like it and I
wanted to see what content that directory had. So I added autoindex feature
to Medusa.
Medusa autoindex features:
- Very similar to that produced by default Apache autoindex feature
(although this patch doesn't produce graphic icons).
- It shows file name, last modified, size and type fields.
- You can order (asc/desc) the list using any displayed field (name,
last modified, size or type).
I've only changed default_handler.py file. I've tried to avoid the
modification of current code as far as I've been able to (in order to avoid
the introduction of bugs in working code). Changes I have made are:
- Functions added to default_handler class:
__get_params
__format_size
__format_entry
__pad_entry
__get_index_order
__sort_output_entry_list
autoindex
- Substituted 'request.error(404) # Not Found' by
'self.autoindex(request.path)' when index.html/default.html is not
found (handle_request method in default_handler class). This is the
link where autoindex is called.
- 'import time' has been added because I needed time conversion tools.
- request['Location'] calculation has been changed. This hasn't got
anything to do with autoindex feature, but I prefer this new way to
calculate it (read the end of the patch to understand the change).
I've developed this feature in SuSE 8.0/Python 2.2. I didn't tried it in
other platforms. If someone tries it, please tell me how it worked.
I've thinking about writting a derived class (autoindex_handler, for
example) to introduce autoindex feature.
Question to Medusa developers/maintainers: In order to include autoindex
feature in Medusa code (if you like it, of course)... do you want me to
write a derived class (autoindex_handler) or do you like the way I did
it (default_handler modification)? What do you prefer? What's better?
If this code is included in Medusa code, it lacks one think. Perhaps you
should include a boolean parameter in __init__ method (default_handler
class): autoindex, for example. If true, autoindex works. If false,
default_handler works as usual (returns 404).
Finally, here comes the patch.
Regards
Sergio
diff -urN medusa-cvs-26-11-2002.orig/default_handler.py medusa-cvs-26-11-2002/default_handler.py
--- medusa-cvs-26-11-2002.orig/default_handler.py Thu Aug 1 20:15:45 2002
+++ medusa-cvs-26-11-2002/default_handler.py Sun Dec 1 13:10:42 2002
@@ -18,6 +18,7 @@
import http_server
import status_handler
import producers
+import time
unquote = http_server.unquote
@@ -72,6 +73,285 @@
def match (self, request):
return 1
+ # get a dictionary from query parameters
+ def __get_params(self,request):
+ [path, params, query, fragment] = request.split_uri()
+ if query == None:
+ return {}
+ out = {}
+ query = string.split(query[1:],"&")
+ for input in query:
+ [key,value] = string.split(input,"=")
+ out[key] = value
+ return out
+
+ # format size to get a pretty output
+ def __format_size(self,input):
+ K = 1024.0
+ M = 1048576.0
+ T = 1073741824.0
+ unit = " "
+ if input >= T:
+ out = input/T
+ unit = "T"
+ elif input >= M:
+ out = input/M
+ unit = "M"
+ elif input >= K:
+ out = input/K
+ unit = "K"
+ else:
+ out = input
+ unit = " "
+
+ if out < 0.1:
+ output = " 0 "
+ elif out < 10:
+ output = "%1.1f%s" % (out,unit)
+ else:
+ output = "%3.0f%s" % (out,unit)
+
+ return output
+
+ # make entry sorter if it's so long.
+ def __format_entry (self,entry,size):
+ if len(entry) > size+3:
+ new_entry = entry[:size] + '..>'
+ else:
+ new_entry = entry[:]
+ return new_entry
+
+ # pad entry with spaces.
+ def __pad_entry (self,entry,size):
+ return ' '*(size-len(entry)+3)
+
+ # calculate what order user wants from input params.
+ def __get_index_order(self,request):
+ params = self.__get_params(request)
+ order = { \
+ 'name' : 0, \
+ 'last_modified' : 0, \
+ 'size' : 0, \
+ 'type' : 0 \
+ }
+
+ if params.has_key('C') and params.has_key('O'):
+ if params['C'] == 'N':
+ if params['O'] == 'A':
+ order['name'] = 1
+ elif params['O'] == 'D':
+ order['name'] = -1
+ elif params['C'] == 'M':
+ if params['O'] == 'A':
+ order['last_modified'] = 1
+ elif params['O'] == 'D':
+ order['last_modified'] = -1
+ elif params['C'] == 'S':
+ if params['O'] == 'A':
+ order['size'] = 1
+ elif params['O'] == 'D':
+ order['size'] = -1
+ elif params['C'] == 'T':
+ if params['O'] == 'A':
+ order['type'] = 1
+ elif params['O'] == 'D':
+ order['type'] = -1
+ else:
+ order['name'] = 1
+
+ return order
+
+ # sort output entry list (as order says)
+ def __sort_output_entry_list(self,list,order):
+ key_list = []
+ output_list = []
+ entry_dict = {}
+ for entry in list:
+ if order['name'] != 0:
+ key = "%s %20d %20d %s" % (
+ entry['name'], \
+ entry['last_modified'], \
+ entry['size'], \
+ entry['type'] \
+ )
+ elif order['last_modified'] != 0:
+ key = "%20d %s %20d %s" % ( \
+ entry['last_modified'], \
+ entry['name'], \
+ entry['size'], \
+ entry['type'] \
+ )
+ elif order['size'] != 0:
+ key = "%20d %2d %s %20d %s" % ( \
+ entry['size'], \
+ 1-entry['is_dir'], \
+ entry['name'], \
+ entry['last_modified'], \
+ entry['type'] \
+ )
+ elif order['type'] != 0:
+ key = "%s %s %20d %20d" % ( \
+ entry['type'], \
+ entry['name'], \
+ entry['size'], \
+ entry['last_modified'] \
+ )
+ else:
+ key = "%s %20d %20d %s" % ( \
+ entry['name'], \
+ entry['last_modified'], \
+ entry['size'], \
+ entry['type'] \
+ )
+
+ key_list.append(key)
+ entry_dict[key] = entry
+
+ key_list.sort()
+
+ order_sum = 0
+ for key in order.keys():
+ order_sum = order_sum + order[key]
+ if order_sum < 0:
+ key_list.reverse()
+
+ for key in key_list:
+ output_list.append(entry_dict[key])
+ return output_list
+
+ # autoindex composition
+ def autoindex (self, request, path):
+ if path and path[-1] == '/':
+ spath = path[:-1]
+ else:
+ spath = path[:]
+ order = self.__get_index_order(request)
+ NAME_SIZE = 20
+ TIME_FORMAT = "%d-%b-%Y %H:%M"
+ input_entry_list = self.filesystem.listdir(spath).list[:]
+ output_entry_list = []
+
+ for input_entry in input_entry_list:
+ full_input_entry = "%s/%s" % (spath,input_entry)
+ stat_input_entry = self.filesystem.stat(full_input_entry)
+
+ output_entry = {}
+
+ if self.filesystem.isdir(full_input_entry):
+ output_entry['is_dir'] = 1
+ else:
+ output_entry['is_dir'] = 0
+
+ output_entry['name'] = input_entry
+
+ output_entry['last_modified'] = stat_input_entry[stat.ST_MTIME]
+
+ if output_entry['is_dir']:
+ output_entry['size'] = 0
+ else:
+ output_entry['size'] = int(stat_input_entry[stat.ST_SIZE])
+
+ if output_entry['is_dir']:
+ output_entry['type'] = ' '
+ else:
+ typ, encoding = mimetypes.guess_type(full_input_entry)
+ if typ is None:
+ typ = 'text/plain'
+ output_entry['type'] = typ
+
+ output_entry_list.append(output_entry)
+
+ output_entry_list = self.__sort_output_entry_list(
+ output_entry_list,
+ order
+ )
+
+ request['Content-Type'] = 'text/html'
+ request.push("<html>\n")
+ request.push("<head>\n")
+ request.push("<title>Index of /%s</title>\n" % spath)
+ request.push("</head>\n")
+ request.push("<body>\n")
+ request.push("<h1>Index of /%s</h1>\n" % spath)
+ request.push("<pre>\n")
+ request.push(" ")
+ if order['name'] == 1:
+ request.push("<a href=\"?C=N&O=D\">")
+ else:
+ request.push("<a href=\"?C=N&O=A\">")
+ request.push("Name</a> ")
+ request.push(self.__pad_entry("Name",NAME_SIZE))
+ if order['last_modified'] == 1:
+ request.push("<a href=\"?C=M&O=D\">")
+ else:
+ request.push("<a href=\"?C=M&O=A\">")
+ request.push("Last modified</a> ")
+ if order['size'] == 1:
+ request.push("<a href=\"?C=S&O=D\">")
+ else:
+ request.push("<a href=\"?C=S&O=A\">")
+ request.push("Size</a> ")
+ if order['type'] == 1:
+ request.push("<a href=\"?C=T&O=D\">")
+ else:
+ request.push("<a href=\"?C=T&O=A\">")
+ request.push("Type</a>")
+ request.push("\n")
+ request.push("<hr>\n")
+ if spath:
+ request.push(" <a href=\"..\">Parent directory</a>\n")
+
+ for output_entry in output_entry_list:
+ if output_entry['is_dir']:
+ output_entry_is_dir = "[D] "
+ else:
+ output_entry_is_dir = " "
+
+ if output_entry['is_dir']:
+ output_entry_name_link = "%s/" % output_entry['name']
+ else:
+ output_entry_name_link = "%s" % output_entry['name']
+
+ output_entry_name_view = self.__format_entry( \
+ output_entry_name_link, \
+ NAME_SIZE \
+ )
+
+ output_entry_last_modified = time.strftime( \
+ TIME_FORMAT, \
+ time.localtime(output_entry['last_modified']) \
+ )
+
+ if output_entry['is_dir']:
+ output_entry_size = ' - '
+ else:
+ output_entry_size = "%s" % self.__format_size(output_entry['size'])
+
+ if output_entry['is_dir']:
+ output_entry_type = ' - '
+ else:
+ output_entry_type = output_entry['type']
+
+ request.push ( \
+ "%s<a href=\"%s\">%s</a>%s %s %s %s\n" % ( \
+ output_entry_is_dir, \
+ output_entry_name_link, \
+ output_entry_name_view, \
+ self.__pad_entry(output_entry_name_view,NAME_SIZE), \
+ output_entry_last_modified, \
+ output_entry_size, \
+ output_entry_type \
+ ) \
+ )
+
+ request.push("<hr>\n")
+ request.push("</pre>\n")
+ request.push("<address>Medusa/%s Server</address>\n" % http_server.VERSION_STRING)
+ request.push("</body>\n")
+ request.push("</html>\n")
+ request.done()
+ return
+
# handle a file request, with caching.
def handle_request (self, request):
@@ -93,8 +373,7 @@
if self.filesystem.isdir (path):
if path and path[-1] != '/':
- request['Location'] = 'http://%s/%s/' % (
- request.channel.server.server_name,
+ request['Location'] = '/%s/' % (
path
)
request.error (301)
@@ -113,7 +392,7 @@
found = 1
break
if not found:
- request.error (404) # Not Found
+ self.autoindex(request,path)
return
elif not self.filesystem.isfile (path):
More information about the Medusa-dev
mailing list