| 1506 | |
| 1507 | class FTPHandler(BaseHandler): |
| 1508 | def ftp_open(self, req): |
| 1509 | import ftplib |
| 1510 | import mimetypes |
| 1511 | host = req.host |
| 1512 | if not host: |
| 1513 | raise URLError('ftp error: no host given') |
| 1514 | host, port = _splitport(host) |
| 1515 | if port is None: |
| 1516 | port = ftplib.FTP_PORT |
| 1517 | else: |
| 1518 | port = int(port) |
| 1519 | |
| 1520 | # username/password handling |
| 1521 | user, host = _splituser(host) |
| 1522 | if user: |
| 1523 | user, passwd = _splitpasswd(user) |
| 1524 | else: |
| 1525 | passwd = None |
| 1526 | host = unquote(host) |
| 1527 | user = user or '' |
| 1528 | passwd = passwd or '' |
| 1529 | |
| 1530 | try: |
| 1531 | host = socket.gethostbyname(host) |
| 1532 | except OSError as msg: |
| 1533 | raise URLError(msg) |
| 1534 | path, attrs = _splitattr(req.selector) |
| 1535 | dirs = path.split('/') |
| 1536 | dirs = list(map(unquote, dirs)) |
| 1537 | dirs, file = dirs[:-1], dirs[-1] |
| 1538 | if dirs and not dirs[0]: |
| 1539 | dirs = dirs[1:] |
| 1540 | fw = None |
| 1541 | try: |
| 1542 | fw = self.connect_ftp(user, passwd, host, port, dirs, req.timeout) |
| 1543 | type = file and 'I' or 'D' |
| 1544 | for attr in attrs: |
| 1545 | attr, value = _splitvalue(attr) |
| 1546 | if attr.lower() == 'type' and \ |
| 1547 | value in ('a', 'A', 'i', 'I', 'd', 'D'): |
| 1548 | type = value.upper() |
| 1549 | fp, retrlen = fw.retrfile(file, type) |
| 1550 | headers = "" |
| 1551 | mtype = mimetypes.guess_type(req.full_url)[0] |
| 1552 | if mtype: |
| 1553 | headers += "Content-type: %s\n" % mtype |
| 1554 | if retrlen is not None and retrlen >= 0: |
| 1555 | headers += "Content-length: %d\n" % retrlen |
| 1556 | headers = email.message_from_string(headers) |
| 1557 | return addinfourl(fp, headers, req.full_url) |
| 1558 | except Exception as exp: |
| 1559 | if fw is not None and not fw.keepalive: |
| 1560 | fw.close() |
| 1561 | if isinstance(exp, ftplib.all_errors): |
| 1562 | raise URLError(f"ftp error: {exp}") from exp |
| 1563 | raise |
| 1564 | |
| 1565 | def connect_ftp(self, user, passwd, host, port, dirs, timeout): |