Ticket #5411: 5411-3.diff

File 5411-3.diff, 10.0 KB (added by adiroiban, 22 months ago)
  • twisted/protocols/ftp.py

    diff --git a/twisted/protocols/ftp.py b/twisted/protocols/ftp.py
    index 19504a7..bb517b6 100644
    a b from twisted.protocols import basic, policies 
    3030 
    3131from twisted.python import log, failure, filepath 
    3232from twisted.python.compat import reduce 
     33from twisted.python.reflect import qual 
    3334 
    3435from twisted.cred import error as cred_error, portal, credentials, checkers 
    3536 
    class DTP(object, protocol.Protocol): 
    417418            self._onConnLost.callback(None) 
    418419 
    419420    def sendLine(self, line): 
     421        """ 
     422        Send a line to data channel. 
     423 
     424        @type  line: L{bytes} 
     425        @param line: The line to be sent. 
     426        """ 
     427        if isinstance(line, unicode): 
     428            warnings.warn( 
     429                "Unicode date received in %s. " 
     430                "Encoded to UTF-8. Please send bytes." % ( 
     431                    qual(self.__class__),), 
     432                category=DeprecationWarning, 
     433                stacklevel=2, 
     434                ) 
     435            line = line.encode('utf-8') 
     436 
    420437        self.transport.write(line + '\r\n') 
    421438 
    422439 
    class FTP(object, basic.LineReceiver, policies.TimeoutMixin): 
    951968        def gotListing(results): 
    952969            self.reply(DATA_CNX_ALREADY_OPEN_START_XFR) 
    953970            for (name, attrs) in results: 
    954                 self.dtpInstance.sendListResponse(name, attrs) 
     971                nameEncoded = self._getEncodedFilename(name) 
     972                self.dtpInstance.sendListResponse(nameEncoded, attrs) 
    955973            self.dtpInstance.transport.loseConnection() 
    956974            return (TXFR_COMPLETE_OK,) 
    957975 
    class FTP(object, basic.LineReceiver, policies.TimeoutMixin): 
    10141032            self.reply(DATA_CNX_ALREADY_OPEN_START_XFR) 
    10151033            for (name, ignored) in results: 
    10161034                if not glob or (glob and fnmatch.fnmatch(name, glob)): 
    1017                     self.dtpInstance.sendLine(name) 
     1035                    nameEncoded = self._getEncodedFilename(name) 
     1036                    self.dtpInstance.sendLine(nameEncoded) 
    10181037            self.dtpInstance.transport.loseConnection() 
    10191038            return (TXFR_COMPLETE_OK,) 
    10201039 
    class FTP(object, basic.LineReceiver, policies.TimeoutMixin): 
    10491068        return d 
    10501069 
    10511070 
     1071    def _getEncodedFilename(self, name): 
     1072        """ 
     1073        Return the UTF-8 encoded filename from an Unicode filename received 
     1074        from IFTPShell.list. 
     1075        """ 
     1076        try: 
     1077            result = name.encode('utf-8') 
     1078        except UnicodeDecodeError: 
     1079            warnings.warn( 
     1080                "Non-Unicode date received in %s from IFTPShell.list. " 
     1081                "Data transmitted without " 
     1082                "encoding it. Please send Unicode." % (qual(self.__class__),), 
     1083                category=DeprecationWarning, 
     1084                stacklevel=1, 
     1085                ) 
     1086            result = name 
     1087        return result 
     1088 
     1089 
    10521090    def ftp_CWD(self, path): 
    10531091        try: 
    10541092            segments = toSegments(self.workingDirectory, path) 
  • twisted/test/test_ftp.py

    diff --git a/twisted/test/test_ftp.py b/twisted/test/test_ftp.py
    index 7ba434e..a61b518 100644
    a b from twisted.internet.interfaces import IConsumer 
    2222from twisted.cred.error import UnauthorizedLogin 
    2323from twisted.cred import portal, checkers, credentials 
    2424from twisted.python import failure, filepath, runtime 
     25from twisted.python.reflect import qual 
    2526from twisted.test import proto_helpers 
    2627 
    2728from twisted.protocols import ftp, loopback 
    class BasicFTPServerTestCase(FTPServerTestCase): 
    513514            ) 
    514515        return d 
    515516 
     517 
     518    def test_getEncodedFilename_unicode(self): 
     519        """ 
     520        When Unicode filenames are received from IFTPShell.list it will 
     521        be encoded to UTF-8. 
     522        """ 
     523        unicodeFilename = u'my resum\xe9' 
     524 
     525        encodedFilename = self.serverProtocol._getEncodedFilename( 
     526            unicodeFilename) 
     527 
     528        self.assertEqual( 
     529            unicodeFilename.encode('utf-8'), encodedFilename) 
     530 
     531 
     532    def test_getEncodedFilename_non_unicode(self): 
     533        """ 
     534        When non-Unicode filenames are received from IFTPShell.list it will 
     535        pass the data without changing it together with raising a warning. 
     536        """ 
     537        alreadyEncodedFilename = u'my resum\xe9'.encode('utf-8') 
     538 
     539        result = self.assertWarns( 
     540            DeprecationWarning, 
     541            "Non-Unicode date received in %s from IFTPShell.list. " 
     542                "Data transmitted without encoding it. " 
     543                "Please send Unicode." % ( 
     544                    qual(self.serverProtocol.__class__)), 
     545            ftp.__file__, 
     546            self.serverProtocol._getEncodedFilename, alreadyEncodedFilename) 
     547 
     548        self.assertIdentical(alreadyEncodedFilename, result) 
     549 
     550 
    516551class FTPServerTestCaseAdvancedClient(FTPServerTestCase): 
    517552    """ 
    518553    Test FTP server with the L{ftp.FTPClient} class. 
    class FTPServerPasvDataConnectionTestCase(FTPServerTestCase): 
    672707            self.assertEqual('', download) 
    673708        return d.addCallback(checkDownload) 
    674709 
     710 
     711    def test_LISTUnicode(self): 
     712        """ 
     713        LIST will receive Unicode filenames from IFTPShell.list, and will 
     714        encode them using UTF-8. 
     715        """ 
     716        d = self._anonymousLogin() 
     717 
     718        def patchedFTPShellList(me, segments): 
     719            """ 
     720            Mock method that patches the IFTPShell.list. 
     721            """ 
     722            return defer.succeed([( 
     723                u'my resum\xe9', (0, 1, 0777, 0, 0, 'user', 'group'))]) 
     724 
     725        def patchFTPShellList(result): 
     726            """ 
     727            Patch the IFTPShell.list, once we got an instance. 
     728            """ 
     729            self.patch(self.serverProtocol.shell, 'list', patchedFTPShellList) 
     730            return result 
     731        d.addCallback(patchFTPShellList) 
     732 
     733        self._download('LIST something', chainDeferred=d) 
     734 
     735        def checkDownload(download): 
     736            self.assertEqual( 
     737                'drwxrwxrwx   0 user      group                   ' 
     738                '0 Jan 01  1970 my resum\xc3\xa9\r\n', 
     739                download) 
     740        return d.addCallback(checkDownload) 
     741 
     742 
    675743    def testManyLargeDownloads(self): 
    676744        # Login 
    677745        d = self._anonymousLogin() 
    class FTPServerPasvDataConnectionTestCase(FTPServerTestCase): 
    756824        return d.addCallback(checkDownload) 
    757825 
    758826 
     827    def test_NLSTUnicode(self): 
     828        """ 
     829        NLST will receive Unicode filenames from IFTPShell.list, and will 
     830        encode them using UTF-8. 
     831        """ 
     832        d = self._anonymousLogin() 
     833 
     834        def patchedFTPShellList(me): 
     835            """ 
     836            Mock method that patches the IFTPShell.list. 
     837            """ 
     838            return defer.succeed([(u'my resum\xe9', None)]) 
     839 
     840        def patchFTPShellList(result): 
     841            """ 
     842            Patch the IFTPShell.list, once we got an instance. 
     843            """ 
     844            self.patch(self.serverProtocol.shell, 'list', patchedFTPShellList) 
     845            return result 
     846        d.addCallback(patchFTPShellList) 
     847 
     848        self._download('NLST something', chainDeferred=d) 
     849 
     850        def checkDownload(download): 
     851            self.assertEqual('my resum\xc3\xa9\r\n', download) 
     852 
     853        return d.addCallback(checkDownload) 
     854 
     855 
    759856    def test_NLSTOnPathToFile(self): 
    760857        """ 
    761858        NLST on an existent file returns only the path to that file. 
    class FTPServerPasvDataConnectionTestCase(FTPServerTestCase): 
    767864        self.dirPath.child('test.txt').touch() 
    768865 
    769866        self._download('NLST test.txt', chainDeferred=d) 
     867 
    770868        def checkDownload(download): 
    771869            filenames = download[:-2].split('\r\n') 
    772870            self.assertEqual(['test.txt'], filenames) 
    class DTPFactoryTests(unittest.TestCase): 
    9821080        return d 
    9831081 
    9841082 
     1083class DTPTests(unittest.TestCase): 
     1084    """ 
     1085    Tests for L{ftp.DTP}. 
     1086 
     1087    The DTP instances in these tests are generated using 
     1088    DTPFactory.buildProtocol() 
     1089    """ 
     1090 
     1091    def setUp(self): 
     1092        """ 
     1093        Create a fake protocol interpreter, a L{ftp.DTPFactory} instance, 
     1094        and dummy transport to help with tests. 
     1095        """ 
     1096        self.reactor = task.Clock() 
     1097 
     1098        class ProtocolInterpreter(object): 
     1099            dtpInstance = None 
     1100 
     1101        self.protocolInterpreter = ProtocolInterpreter() 
     1102        self.factory = ftp.DTPFactory( 
     1103            self.protocolInterpreter, None, self.reactor) 
     1104        self.transport = proto_helpers.StringTransportWithDisconnection() 
     1105 
     1106 
     1107    def test_sendLine_newline(self): 
     1108        """ 
     1109        When sending a line, the newline delimiter will be automatically 
     1110        added. 
     1111        """ 
     1112        dtpInstance = self.factory.buildProtocol(None) 
     1113        dtpInstance.makeConnection(self.transport) 
     1114        lineContent = 'line content' 
     1115 
     1116        dtpInstance.sendLine(lineContent) 
     1117 
     1118        dataSent = self.transport.value() 
     1119        self.assertEqual(lineContent + '\r\n', dataSent) 
     1120 
     1121 
     1122    def test_sendLine_unicode(self): 
     1123        """ 
     1124        When sending an unicode line, it will be converted to str and 
     1125        a warning is raised. 
     1126        """ 
     1127        from twisted.trial import _synctest 
     1128        dtpInstance = self.factory.buildProtocol(None) 
     1129        dtpInstance.makeConnection(self.transport) 
     1130        lineContent = u'my resum\xe9' 
     1131 
     1132        self.assertWarns( 
     1133            DeprecationWarning, 
     1134            "Unicode date received in %s. " 
     1135                "Encoded to UTF-8. Please send bytes." % ( 
     1136                    qual(dtpInstance.__class__)), 
     1137            _synctest.__file__, 
     1138            dtpInstance.sendLine, lineContent) 
     1139 
     1140        dataSent = self.transport.value() 
     1141        self.assertTrue(isinstance(dataSent, str)) 
     1142        self.assertEqual(lineContent.encode('utf-8') + '\r\n', dataSent) 
     1143 
     1144 
    9851145 
    9861146# -- Client Tests ----------------------------------------------------------- 
    9871147 
  • new file twisted/topfiles/5411.bugfix

    diff --git a/twisted/topfiles/5411.bugfix b/twisted/topfiles/5411.bugfix
    new file mode 100644
    index 0000000..bce9487
    - +  
     1twisted.protocols.ftp.FTP 'ftp_LIST' and 'ftp_NLST' will encode to UTF-8 all Unicode filenames received from IFTPShell. 
     2 No newline at end of file