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)
| 807 | raise |
| 808 | |
| 809 | def list_directory(self, path): |
| 810 | """Helper to produce a directory listing (absent index.html). |
| 811 | |
| 812 | Return value is either a file object, or None (indicating an |
| 813 | error). In either case, the headers are sent, making the |
| 814 | interface the same as for send_head(). |
| 815 | |
| 816 | """ |
| 817 | try: |
| 818 | list = os.listdir(path) |
| 819 | except OSError: |
| 820 | self.send_error( |
| 821 | HTTPStatus.NOT_FOUND, |
| 822 | "No permission to list directory") |
| 823 | return None |
| 824 | list.sort(key=lambda a: a.lower()) |
| 825 | r = [] |
| 826 | displaypath = self.path |
| 827 | displaypath = displaypath.split('#', 1)[0] |
| 828 | displaypath = displaypath.split('?', 1)[0] |
| 829 | try: |
| 830 | displaypath = urllib.parse.unquote(displaypath, |
| 831 | errors='surrogatepass') |
| 832 | except UnicodeDecodeError: |
| 833 | displaypath = urllib.parse.unquote(displaypath) |
| 834 | displaypath = html.escape(displaypath, quote=False) |
| 835 | enc = sys.getfilesystemencoding() |
| 836 | title = f'Directory listing for {displaypath}' |
| 837 | r.append('<!DOCTYPE HTML>') |
| 838 | r.append('<html lang="en">') |
| 839 | r.append('<head>') |
| 840 | r.append(f'<meta charset="{enc}">') |
| 841 | r.append('<style type="text/css">\n:root {\ncolor-scheme: light dark;\n}\n</style>') |
| 842 | r.append(f'<title>{title}</title>\n</head>') |
| 843 | r.append(f'<body>\n<h1>{title}</h1>') |
| 844 | r.append('<hr>\n<ul>') |
| 845 | for name in list: |
| 846 | fullname = os.path.join(path, name) |
| 847 | displayname = linkname = name |
| 848 | # Append / for directories or @ for symbolic links |
| 849 | if os.path.isdir(fullname): |
| 850 | displayname = name + "/" |
| 851 | linkname = name + "/" |
| 852 | if os.path.islink(fullname): |
| 853 | displayname = name + "@" |
| 854 | # Note: a link to a directory displays with @ and links with / |
| 855 | r.append('<li><a href="%s">%s</a></li>' |
| 856 | % (urllib.parse.quote(linkname, |
| 857 | errors='surrogatepass'), |
| 858 | html.escape(displayname, quote=False))) |
| 859 | r.append('</ul>\n<hr>\n</body>\n</html>\n') |
| 860 | encoded = '\n'.join(r).encode(enc, 'surrogateescape') |
| 861 | f = io.BytesIO() |
| 862 | f.write(encoded) |
| 863 | f.seek(0) |
| 864 | self.send_response(HTTPStatus.OK) |
| 865 | self.send_header("Content-type", "text/html; charset=%s" % enc) |
| 866 | self.send_header("Content-Length", str(len(encoded))) |
no test coverage detected