Helper to produce a directory listing (absent index.html). Return value is either a file object, or None (indicating an error). In either case, the headers are sent, making the interface the same as for send_head().
(self, path)
| 835 | raise |
| 836 | |
| 837 | def list_directory(self, path): |
| 838 | """Helper to produce a directory listing (absent index.html). |
| 839 | |
| 840 | Return value is either a file object, or None (indicating an |
| 841 | error). In either case, the headers are sent, making the |
| 842 | interface the same as for send_head(). |
| 843 | |
| 844 | """ |
| 845 | try: |
| 846 | list = os.listdir(path) |
| 847 | except OSError: |
| 848 | self.send_error( |
| 849 | HTTPStatus.NOT_FOUND, |
| 850 | "No permission to list directory") |
| 851 | return None |
| 852 | list.sort(key=lambda a: a.lower()) |
| 853 | r = [] |
| 854 | displaypath = self.path |
| 855 | displaypath = displaypath.split('#', 1)[0] |
| 856 | displaypath = displaypath.split('?', 1)[0] |
| 857 | try: |
| 858 | displaypath = urllib.parse.unquote(displaypath, |
| 859 | errors='surrogatepass') |
| 860 | except UnicodeDecodeError: |
| 861 | displaypath = urllib.parse.unquote(displaypath) |
| 862 | displaypath = html.escape(displaypath, quote=False) |
| 863 | enc = sys.getfilesystemencoding() |
| 864 | title = f'Directory listing for {displaypath}' |
| 865 | r.append('<!DOCTYPE HTML>') |
| 866 | r.append('<html lang="en">') |
| 867 | r.append('<head>') |
| 868 | r.append(f'<meta charset="{enc}">') |
| 869 | r.append('<style type="text/css">\n:root {\ncolor-scheme: light dark;\n}\n</style>') |
| 870 | r.append(f'<title>{title}</title>\n</head>') |
| 871 | r.append(f'<body>\n<h1>{title}</h1>') |
| 872 | r.append('<hr>\n<ul>') |
| 873 | for name in list: |
| 874 | fullname = os.path.join(path, name) |
| 875 | displayname = linkname = name |
| 876 | # Append / for directories or @ for symbolic links |
| 877 | if os.path.isdir(fullname): |
| 878 | displayname = name + "/" |
| 879 | linkname = name + "/" |
| 880 | if os.path.islink(fullname): |
| 881 | displayname = name + "@" |
| 882 | # Note: a link to a directory displays with @ and links with / |
| 883 | r.append('<li><a href="%s">%s</a></li>' |
| 884 | % (urllib.parse.quote(linkname, |
| 885 | errors='surrogatepass'), |
| 886 | html.escape(displayname, quote=False))) |
| 887 | r.append('</ul>\n<hr>\n</body>\n</html>\n') |
| 888 | encoded = '\n'.join(r).encode(enc, 'surrogateescape') |
| 889 | f = io.BytesIO() |
| 890 | f.write(encoded) |
| 891 | f.seek(0) |
| 892 | self.send_response(HTTPStatus.OK) |
| 893 | self.send_header("Content-type", "text/html; charset=%s" % enc) |
| 894 | self.send_header("Content-Length", str(len(encoded))) |
no test coverage detected