source: ftputil/session.py @ 1501:cc42f6ab9d3e

Last change on this file since 1501:cc42f6ab9d3e was 1501:cc42f6ab9d3e, checked in by Stefan Schwarzer <sschwarzer@…>, 7 years ago
Encrypt data channel by default if a method `prot_p` exists. I think most users will want to secure the data channel, so rather be safe than sorry and turn the encryption on by default. If needed, the data channel encryption can be suppressed by passing `encrypt_data_channel=False`.
File size: 3.5 KB
Line 
1# Copyright (C) 2014, Stefan Schwarzer
2
3"""
4Session factory factory (the two "factory" are intential :-) )
5for ftputil.
6"""
7
8from __future__ import unicode_literals
9
10import ftplib
11
12import ftputil.tool
13
14try:
15    import M2Crypto
16    import M2Crypto.ftpslib
17except ImportError:
18    M2Crypto = None
19
20
21__all__ = ["session_factory"]
22
23
24def session_factory(base_class=ftplib.FTP, port=21, use_passive_mode=None,
25                    encrypt_data_channel=True, debug_level=None):
26    """
27    Create and return a session factory according to the keyword
28    arguments.
29
30    base_class: Base class to use for the session class (e. g.
31    `ftplib.FTP_TLS` or `M2Crypto.ftpslib.FTP_TLS`, default is
32    `ftplib.FTP`).
33
34    port: Port number (integer) for the command channel (default 21).
35    If you don't know what "command channel" means, use the default or
36    use what the provider gave you as "the FTP port".
37
38    use_passive_mode: If `True`, explicitly use passive mode. If
39    `False`, explicitly don't use passive mode. If `None` (default),
40    let the `base_class` decide whether it wants to use active or
41    passive mode.
42
43    encrypt_data_channel: If `True` (the default), call the `prot_p`
44    method of the base class if it has the method. If `False` or
45    `None` (`None` is the default), don't call the method.
46
47    debug_level: Debug level (integer) to be set on a session
48    instance. The default is `None`, meaning no debugging output.
49
50    This function should work the base classes for `ftplib.FTP`,
51    `ftplib.FTP_TLS` and `M2Crypto.ftpslib.FTP_TLS` with TLS security.
52    Other base classes should work if they use the same API as
53    `ftplib.FTP`.
54
55    Usage example:
56
57      my_session_factory = session_factory(
58                             base_class=M2Crypto.ftpslib.FTP_TLS,
59                             use_passive_mode=True,
60                             encrypt_data_channel=True)
61      with ftputil.FTPHost(host, user, password,
62                           session_factory=my_session_factory) as host:
63        ...
64    """
65    class Session(base_class):
66        """Session factory class created by `session_factory`."""
67
68        def __init__(self, host, user, password):
69            base_class.__init__(self)
70            self.connect(host, port)
71            if self._use_m2crypto_ftpslib():
72                self.auth_tls()
73                self._fix_socket()
74            if debug_level is not None:
75                self.set_debuglevel(debug_level)
76            self.login(user, password)
77            if use_passive_mode is not None:
78                self.set_pasv(use_passive_mode)
79            if encrypt_data_channel and hasattr(base_class, "prot_p"):
80                self.prot_p()
81
82        def _use_m2crypto_ftpslib(self):
83            """
84            Return `True` if the base class to use is
85            `M2Crypto.ftpslib.FTP_TLS`, else return `False`.
86            """
87            return (M2Crypto is not None and
88                    issubclass(base_class, M2Crypto.ftpslib.FTP_TLS))
89
90        def _fix_socket(self):
91            """
92            Change the socket object so that arguments to `sendall`
93            are converted to byte strings before being used.
94
95            See the ftputil ticket #78 for details:
96            http://ftputil.sschwarzer.net/trac/ticket/78
97            """
98            original_sendall = self.sock.sendall
99            # Bound method, therefore no `self` argument.
100            def sendall(data):
101                data = ftputil.tool.as_bytes(data)
102                return original_sendall(data)
103            self.sock.sendall = sendall
104
105    return Session
Note: See TracBrowser for help on using the repository browser.