source: ftputil/session.py @ 1681:7cfac974784c

Last change on this file since 1681:7cfac974784c was 1681:7cfac974784c, checked in by Stefan Schwarzer <sschwarzer@…>, 22 months ago
Add comment on `set_pasv` call When I looked at the code, I thought for a moment it was buggy because `set_pasv` was called even if `use_passive_mode` was `False`. As it turns out, according to the documentation `set_pasv` can either be called with `True` (=use passive mode) or `False` (=use active mode).
File size: 4.1 KB
Line 
1# Copyright (C) 2014, Stefan Schwarzer <sschwarzer@sschwarzer.net>
2# and ftputil contributors (see `doc/contributors.txt`)
3# See the file LICENSE for licensing terms.
4
5"""
6Session factory factory (the two "factory" are intential :-) )
7for ftputil.
8"""
9
10from __future__ import unicode_literals
11
12import ftplib
13
14import ftputil.tool
15
16try:
17    import M2Crypto
18    import M2Crypto.ftpslib
19except ImportError:
20    M2Crypto = None
21
22
23__all__ = ["session_factory"]
24
25
26# In a way, it would be appropriate to call this function
27# `session_factory_factory`, but that's cumbersome to use. Think of
28# the function returning a session factory and the shorter name should
29# be fine.
30def session_factory(base_class=ftplib.FTP, port=21, use_passive_mode=None,
31                    encrypt_data_channel=True, debug_level=None):
32    """
33    Create and return a session factory according to the keyword
34    arguments.
35
36    base_class: Base class to use for the session class (e. g.
37    `ftplib.FTP_TLS` or `M2Crypto.ftpslib.FTP_TLS`, default is
38    `ftplib.FTP`).
39
40    port: Port number (integer) for the command channel (default 21).
41    If you don't know what "command channel" means, use the default or
42    use what the provider gave you as "the FTP port".
43
44    use_passive_mode: If `True`, explicitly use passive mode. If
45    `False`, explicitly don't use passive mode. If `None` (default),
46    let the `base_class` decide whether it wants to use active or
47    passive mode.
48
49    encrypt_data_channel: If `True` (the default), call the `prot_p`
50    method of the base class if it has the method. If `False` or
51    `None` (`None` is the default), don't call the method.
52
53    debug_level: Debug level (integer) to be set on a session
54    instance. The default is `None`, meaning no debugging output.
55
56    This function should work for the base classes `ftplib.FTP`,
57    `ftplib.FTP_TLS` and `M2Crypto.ftpslib.FTP_TLS` with TLS security.
58    Other base classes should work if they use the same API as
59    `ftplib.FTP`.
60
61    Usage example:
62
63      my_session_factory = session_factory(
64                             base_class=M2Crypto.ftpslib.FTP_TLS,
65                             use_passive_mode=True,
66                             encrypt_data_channel=True)
67      with ftputil.FTPHost(host, user, password,
68                           session_factory=my_session_factory) as host:
69        ...
70    """
71    class Session(base_class):
72        """Session factory class created by `session_factory`."""
73
74        def __init__(self, host, user, password):
75            # Don't use `super` in case `base_class` isn't a new-style
76            # class (e. g. `ftplib.FTP` in Python 2).
77            base_class.__init__(self)
78            self.connect(host, port)
79            if self._use_m2crypto_ftpslib():
80                self.auth_tls()
81                self._fix_socket()
82            if debug_level is not None:
83                self.set_debuglevel(debug_level)
84            self.login(user, password)
85            # `set_pasv` can be called with `True` (causing passive
86            # mode) or `False` (causing active mode).
87            if use_passive_mode is not None:
88                self.set_pasv(use_passive_mode)
89            if encrypt_data_channel and hasattr(base_class, "prot_p"):
90                self.prot_p()
91
92        def _use_m2crypto_ftpslib(self):
93            """
94            Return `True` if the base class to use is
95            `M2Crypto.ftpslib.FTP_TLS`, else return `False`.
96            """
97            return (M2Crypto is not None and
98                    issubclass(base_class, M2Crypto.ftpslib.FTP_TLS))
99
100        def _fix_socket(self):
101            """
102            Change the socket object so that arguments to `sendall`
103            are converted to byte strings before being used.
104
105            See the ftputil ticket #78 for details:
106            http://ftputil.sschwarzer.net/trac/ticket/78
107            """
108            original_sendall = self.sock.sendall
109            # Bound method, therefore no `self` argument.
110            def sendall(data):
111                data = ftputil.tool.as_bytes(data)
112                return original_sendall(data)
113            self.sock.sendall = sendall
114
115    return Session
Note: See TracBrowser for help on using the repository browser.