ftpserver.py.rej 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207
  1. --- pyftpdlib/ftpserver.py
  2. +++ pyftpdlib/ftpserver.py
  3. @@ -709,8 +709,8 @@
  4. self.set_reuse_addr()
  5. try:
  6. self.bind((local_ip, port))
  7. - except socket.error, why:
  8. - if why[0] == errno.EADDRINUSE: # port already in use
  9. + except socket.error, err:
  10. + if err[0] == errno.EADDRINUSE: # port already in use
  11. if ports:
  12. continue
  13. # If cannot use one of the ports in the configured
  14. @@ -1306,9 +1306,11 @@
  15. if self.read_limit:
  16. while self.ac_in_buffer_size > self.read_limit:
  17. self.ac_in_buffer_size /= 2
  18. + self.ac_in_buffer_size = int(self.ac_in_buffer_size)
  19. if self.write_limit:
  20. while self.ac_out_buffer_size > self.write_limit:
  21. self.ac_out_buffer_size /= 2
  22. + self.ac_out_buffer_size = int(self.ac_out_buffer_size)
  23. def _use_sendfile(self, producer):
  24. return False
  25. @@ -1403,8 +1405,9 @@
  26. # returning some data
  27. loops = 20
  28. - def __init__(self, iterator):
  29. + def __init__(self, iterator, encoding=None):
  30. self.iterator = iterator
  31. + self.encoding = encoding
  32. def more(self):
  33. """Attempt a chunk of data from iterator by calling
  34. @@ -1416,7 +1419,12 @@
  35. buffer.append(self.iterator.next())
  36. except StopIteration:
  37. break
  38. - return ''.join(buffer)
  39. + if self.encoding is None:
  40. + return ''.join(buffer)
  41. + else:
  42. + data = ''.join(buffer)
  43. + data = data.encode(self.encoding)
  44. + return data
  45. # --- filesystem
  46. @@ -2007,6 +2015,10 @@
  47. use_sendfile = sendfile is not None
  48. tcp_no_delay = hasattr(socket, "TCP_NODELAY")
  49. + #
  50. + use_encoding = True
  51. + encoding = "utf-8"
  52. +
  53. def __init__(self, conn, server):
  54. """Initialize the command channel.
  55. @@ -2014,6 +2026,9 @@
  56. established connection.
  57. - (instance) server: the ftp server class instance.
  58. """
  59. + asynchat.async_chat.__init__(self, conn)
  60. + self.set_terminator("\r\n")
  61. +
  62. # public session attributes
  63. self.server = server
  64. self.fs = None
  65. @@ -2080,6 +2095,7 @@
  66. # #100) while EINVAL can occur on OSX (see issue #143).
  67. self.connected = False
  68. if err[0] in (errno.ENOTCONN, errno.EINVAL):
  69. +# if err.errno == errno.ENOTCONN:
  70. self.close()
  71. else:
  72. self.handle_error()
  73. @@ -2177,6 +2193,23 @@
  74. self._in_buffer = []
  75. self._in_buffer_len = 0
  76. + def decode_received_line(self, line):
  77. + """Decode the received cmd + arg from bytes to a unicode string.
  78. + You might want to override this method to attempt to convert the
  79. + line by using different encodings in case UTF8 fails for some
  80. + reason (e.g. clients not following RFC-2640).
  81. +
  82. + Example:
  83. +
  84. + try:
  85. + return line.decode('utf8')
  86. + except UnicodeDecodeError:
  87. + return line.decode('latin1')
  88. + """
  89. + if not self.encoding:
  90. + return line
  91. + return line.decode(self.encoding)
  92. +
  93. def found_terminator(self):
  94. r"""Called when the incoming data stream matches the \r\n
  95. terminator.
  96. @@ -2187,6 +2220,13 @@
  97. line = ''.join(self._in_buffer)
  98. self._in_buffer = []
  99. self._in_buffer_len = 0
  100. + try:
  101. + line = self.decode_received_line(line)
  102. + except UnicodeDecodeError:
  103. + self.respond("501 Can't decode the received command. This server "
  104. + "is using %s encoding. Make sure your client does "
  105. + "the same." %self.encoding)
  106. + return
  107. cmd = line.split(' ')[0].upper()
  108. arg = line[len(cmd)+1:]
  109. @@ -2317,8 +2357,8 @@
  110. if hasattr(socket, 'MSG_OOB'):
  111. try:
  112. data = self.socket.recv(1024, socket.MSG_OOB)
  113. - except socket.error, why:
  114. - if why[0] == errno.EINVAL:
  115. + except socket.error, err:
  116. + if err[0] == errno.EINVAL:
  117. return
  118. else:
  119. self._in_buffer.append(data)
  120. @@ -2485,6 +2525,11 @@
  121. # --- utility
  122. + def push(self, resp):
  123. + if self.encoding:
  124. + resp = resp.encode(self.encoding)
  125. + asynchat.async_chat.push(self, resp)
  126. +
  127. def respond(self, resp):
  128. """Send a response to the client using the command channel."""
  129. self._last_response = resp
  130. @@ -2609,7 +2654,7 @@
  131. further commands.
  132. """
  133. if cmd in ("DELE", "RMD", "RNFR", "RNTO", "MKD"):
  134. - line = '"%s" %s' % (' '.join([cmd, str(arg)]).strip(), respcode)
  135. + line = '"%s" %s' % (' '.join([cmd, arg]).strip(), respcode)
  136. self.log(line)
  137. def log_transfer(self, cmd, filename, receive, completed, elapsed, bytes):
  138. @@ -2873,7 +2918,7 @@
  139. why = _strerror(err)
  140. self.respond('550 %s.' % why)
  141. else:
  142. - producer = BufferedIteratorProducer(iterator)
  143. + producer = BufferedIteratorProducer(iterator, self.encoding )
  144. self.push_dtp_data(producer, isproducer=True, cmd="LIST")
  145. def ftp_NLST(self, path):
  146. @@ -2894,7 +2939,10 @@
  147. if listing:
  148. listing.sort()
  149. data = '\r\n'.join(listing) + '\r\n'
  150. - self.push_dtp_data(data, cmd="NLST")
  151. + if self.encoding:
  152. + data = data.encode(self.encoding)
  153. + self.log('OK NLST "%s". Transfer starting.' % path)
  154. + self.push_dtp_data(data)
  155. # --- MLST and MLSD commands
  156. @@ -2943,7 +2991,7 @@
  157. perms = self.authorizer.get_perms(self.username)
  158. iterator = self.fs.format_mlsx(path, listing, perms,
  159. self._current_facts)
  160. - producer = BufferedIteratorProducer(iterator)
  161. + producer = BufferedIteratorProducer(iterator, self.encoding)
  162. self.push_dtp_data(producer, isproducer=True, cmd="MLSD")
  163. def ftp_RETR(self, file):
  164. @@ -3489,7 +3537,8 @@
  165. self.respond('550 %s.' %why)
  166. else:
  167. self.push('213-Status of "%s":\r\n' % line)
  168. - self.push_with_producer(BufferedIteratorProducer(iterator))
  169. + self.push_with_producer(BufferedIteratorProducer(iterator,
  170. + self.encoding))
  171. self.respond('213 End of status.')
  172. def ftp_FEAT(self, line):
  173. @@ -3497,6 +3546,8 @@
  174. features = ['TVFS']
  175. features += [feat for feat in ('EPRT', 'EPSV', 'MDTM', 'SIZE') \
  176. if feat in self.proto_cmds]
  177. + if self.encoding and self.encoding.lower() in ('utf8, utf-8'):
  178. + features.append('UTF8')
  179. features.extend(self._extra_feats)
  180. if 'MLST' in self.proto_cmds or 'MLSD' in self.proto_cmds:
  181. facts = ''
  182. @@ -3779,7 +3830,7 @@
  183. return
  184. except socket.error, err:
  185. # ECONNABORTED might be thrown on *BSD (see issue 105)
  186. - if err[0] != errno.ECONNABORTED:
  187. + if err.errno != errno.ECONNABORTED:
  188. logerror(traceback.format_exc())
  189. return
  190. else: