add proxy support powered by PySocks
Read proxy option in imapserver, instantiate a class in imaplibutil using a self-defined keyword and a socket instance, and use this socket instance to substitute the default socket instance used in imaplib2. Signed-off-by: 夏恺(Xia Kai) <xiaket@gmail.com> Signed-off-by: Nicolas Sebrecht <nicolas.s-dev@laposte.net>
This commit is contained in:
parent
a1383da9b3
commit
d36209a305
@ -380,6 +380,15 @@ remoterepository = RemoteExample
|
|||||||
#filterheaders = X-Some-Weird-Header
|
#filterheaders = X-Some-Weird-Header
|
||||||
|
|
||||||
|
|
||||||
|
# This option stands in the [Account Test] section.
|
||||||
|
#
|
||||||
|
# Use proxy connection for this account. Usefull to bypass the GFW in China.
|
||||||
|
# To specify a proxy connection, join proxy type, host and port with colons.
|
||||||
|
# Available proxy types are SOCKS5, SOCKS4, HTTP.
|
||||||
|
# You also need to install PySocks through pip.
|
||||||
|
#
|
||||||
|
#proxy = SOCKS5:localhost:9999
|
||||||
|
|
||||||
[Repository LocalExample]
|
[Repository LocalExample]
|
||||||
|
|
||||||
# Each repository requires a "type" declaration. The types supported for
|
# Each repository requires a "type" declaration. The types supported for
|
||||||
|
@ -21,6 +21,7 @@ import subprocess
|
|||||||
from sys import exc_info
|
from sys import exc_info
|
||||||
import threading
|
import threading
|
||||||
from hashlib import sha1
|
from hashlib import sha1
|
||||||
|
import socket
|
||||||
|
|
||||||
from offlineimap.ui import getglobalui
|
from offlineimap.ui import getglobalui
|
||||||
from offlineimap import OfflineImapError
|
from offlineimap import OfflineImapError
|
||||||
@ -69,6 +70,37 @@ class UsefulIMAPMixIn(object):
|
|||||||
def _mesg(self, s, tn=None, secs=None):
|
def _mesg(self, s, tn=None, secs=None):
|
||||||
new_mesg(self, s, tn, secs)
|
new_mesg(self, s, tn, secs)
|
||||||
|
|
||||||
|
# Overrides private function from IMAP4 (@imaplib2)
|
||||||
|
def open_socket(self):
|
||||||
|
"""open_socket()
|
||||||
|
Open socket choosing first address family available."""
|
||||||
|
msg = (-1, 'could not open socket')
|
||||||
|
for res in socket.getaddrinfo(self.host, self.port, socket.AF_UNSPEC, socket.SOCK_STREAM):
|
||||||
|
af, socktype, proto, canonname, sa = res
|
||||||
|
try:
|
||||||
|
# use socket of our own, possiblly socksified socket.
|
||||||
|
s = self.socket(af, socktype, proto)
|
||||||
|
except socket.error, msg:
|
||||||
|
continue
|
||||||
|
try:
|
||||||
|
for i in (0, 1):
|
||||||
|
try:
|
||||||
|
s.connect(sa)
|
||||||
|
break
|
||||||
|
except socket.error, msg:
|
||||||
|
if len(msg.args) < 2 or msg.args[0] != errno.EINTR:
|
||||||
|
raise
|
||||||
|
else:
|
||||||
|
raise socket.error(msg)
|
||||||
|
except socket.error, msg:
|
||||||
|
s.close()
|
||||||
|
continue
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
raise socket.error(msg)
|
||||||
|
|
||||||
|
return s
|
||||||
|
|
||||||
|
|
||||||
class IMAP4_Tunnel(UsefulIMAPMixIn, IMAP4):
|
class IMAP4_Tunnel(UsefulIMAPMixIn, IMAP4):
|
||||||
"""IMAP4 client class over a tunnel
|
"""IMAP4 client class over a tunnel
|
||||||
@ -79,6 +111,9 @@ class IMAP4_Tunnel(UsefulIMAPMixIn, IMAP4):
|
|||||||
The result will be in PREAUTH stage."""
|
The result will be in PREAUTH stage."""
|
||||||
|
|
||||||
def __init__(self, tunnelcmd, **kwargs):
|
def __init__(self, tunnelcmd, **kwargs):
|
||||||
|
if "use_socket" in kwargs:
|
||||||
|
self.socket = kwargs['use_socket']
|
||||||
|
del kwargs['use_socket']
|
||||||
IMAP4.__init__(self, tunnelcmd, **kwargs)
|
IMAP4.__init__(self, tunnelcmd, **kwargs)
|
||||||
|
|
||||||
def open(self, host, port):
|
def open(self, host, port):
|
||||||
@ -141,6 +176,9 @@ class WrappedIMAP4_SSL(UsefulIMAPMixIn, IMAP4_SSL):
|
|||||||
"""Improved version of imaplib.IMAP4_SSL overriding select()."""
|
"""Improved version of imaplib.IMAP4_SSL overriding select()."""
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
|
if "use_socket" in kwargs:
|
||||||
|
self.socket = kwargs['use_socket']
|
||||||
|
del kwargs['use_socket']
|
||||||
self._fingerprint = kwargs.get('fingerprint', None)
|
self._fingerprint = kwargs.get('fingerprint', None)
|
||||||
if type(self._fingerprint) != type([]):
|
if type(self._fingerprint) != type([]):
|
||||||
self._fingerprint = [self._fingerprint]
|
self._fingerprint = [self._fingerprint]
|
||||||
@ -171,7 +209,11 @@ class WrappedIMAP4_SSL(UsefulIMAPMixIn, IMAP4_SSL):
|
|||||||
class WrappedIMAP4(UsefulIMAPMixIn, IMAP4):
|
class WrappedIMAP4(UsefulIMAPMixIn, IMAP4):
|
||||||
"""Improved version of imaplib.IMAP4 overriding select()."""
|
"""Improved version of imaplib.IMAP4 overriding select()."""
|
||||||
|
|
||||||
pass
|
def __init__(self, *args, **kwargs):
|
||||||
|
if "use_socket" in kwargs:
|
||||||
|
self.socket = kwargs['use_socket']
|
||||||
|
del kwargs['use_socket']
|
||||||
|
IMAP4.__init__(self, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
def Internaldate2epoch(resp):
|
def Internaldate2epoch(resp):
|
||||||
|
@ -15,10 +15,7 @@
|
|||||||
# along with this program; if not, write to the Free Software
|
# along with this program; if not, write to the Free Software
|
||||||
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
|
||||||
from offlineimap import imaplibutil, imaputil, threadutil, OfflineImapError
|
|
||||||
from offlineimap.ui import getglobalui
|
|
||||||
from threading import Lock, BoundedSemaphore, Thread, Event, currentThread
|
from threading import Lock, BoundedSemaphore, Thread, Event, currentThread
|
||||||
import offlineimap.accounts
|
|
||||||
import hmac
|
import hmac
|
||||||
import socket
|
import socket
|
||||||
import base64
|
import base64
|
||||||
@ -28,6 +25,11 @@ from sys import exc_info
|
|||||||
from socket import gaierror
|
from socket import gaierror
|
||||||
from ssl import SSLError, cert_time_to_seconds
|
from ssl import SSLError, cert_time_to_seconds
|
||||||
|
|
||||||
|
from offlineimap import imaplibutil, imaputil, threadutil, OfflineImapError
|
||||||
|
import offlineimap.accounts
|
||||||
|
from offlineimap.ui import getglobalui
|
||||||
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# do we have a recent pykerberos?
|
# do we have a recent pykerberos?
|
||||||
have_gss = False
|
have_gss = False
|
||||||
@ -101,6 +103,31 @@ class IMAPServer:
|
|||||||
self.gss_vc = None
|
self.gss_vc = None
|
||||||
self.gssapi = False
|
self.gssapi = False
|
||||||
|
|
||||||
|
# In order to support proxy connection, we have to override the
|
||||||
|
# default socket instance with our own socksified socket instance.
|
||||||
|
# We add this option to bypass the GFW in China.
|
||||||
|
_account_section = 'Account ' + self.repos.account.name
|
||||||
|
if not self.config.has_option(_account_section, 'proxy'):
|
||||||
|
self.proxied_socket = socket.socket
|
||||||
|
else:
|
||||||
|
proxy = self.config.get(_account_section, 'proxy')
|
||||||
|
# Powered by PySocks.
|
||||||
|
try:
|
||||||
|
import socks
|
||||||
|
proxy_type, host, port = proxy.split(":")
|
||||||
|
port = int(port)
|
||||||
|
socks.setdefaultproxy(getattr(socks, proxy_type), host, port)
|
||||||
|
self.proxied_socket = socks.socksocket
|
||||||
|
except ImportError:
|
||||||
|
self.ui.warn("PySocks not installed, ignoring proxy option.")
|
||||||
|
self.proxied_socket = socket.socket
|
||||||
|
except (AttributeError, ValueError) as e:
|
||||||
|
self.ui.warn("Bad proxy option %s for account %s: %s "
|
||||||
|
"Ignoring proxy option."%
|
||||||
|
(proxy, self.repos.account.name, e))
|
||||||
|
self.proxied_socket = socket.socket
|
||||||
|
|
||||||
|
|
||||||
def __getpassword(self):
|
def __getpassword(self):
|
||||||
"""Returns the server password or None"""
|
"""Returns the server password or None"""
|
||||||
if self.goodpassword != None: # use cached good one first
|
if self.goodpassword != None: # use cached good one first
|
||||||
@ -391,25 +418,33 @@ class IMAPServer:
|
|||||||
# Generate a new connection.
|
# Generate a new connection.
|
||||||
if self.tunnel:
|
if self.tunnel:
|
||||||
self.ui.connecting('tunnel', self.tunnel)
|
self.ui.connecting('tunnel', self.tunnel)
|
||||||
imapobj = imaplibutil.IMAP4_Tunnel(self.tunnel,
|
imapobj = imaplibutil.IMAP4_Tunnel(
|
||||||
timeout=socket.getdefaulttimeout())
|
self.tunnel,
|
||||||
|
timeout=socket.getdefaulttimeout(),
|
||||||
|
use_socket=self.proxied_socket,
|
||||||
|
)
|
||||||
success = 1
|
success = 1
|
||||||
elif self.usessl:
|
elif self.usessl:
|
||||||
self.ui.connecting(self.hostname, self.port)
|
self.ui.connecting(self.hostname, self.port)
|
||||||
imapobj = imaplibutil.WrappedIMAP4_SSL(self.hostname,
|
imapobj = imaplibutil.WrappedIMAP4_SSL(
|
||||||
self.port,
|
self.hostname,
|
||||||
self.sslclientkey,
|
self.port,
|
||||||
self.sslclientcert,
|
self.sslclientkey,
|
||||||
self.sslcacertfile,
|
self.sslclientcert,
|
||||||
self.__verifycert,
|
self.sslcacertfile,
|
||||||
self.sslversion,
|
self.__verifycert,
|
||||||
timeout=socket.getdefaulttimeout(),
|
self.sslversion,
|
||||||
fingerprint=self.fingerprint
|
timeout=socket.getdefaulttimeout(),
|
||||||
)
|
fingerprint=self.fingerprint,
|
||||||
|
use_socket=self.proxied_socket,
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
self.ui.connecting(self.hostname, self.port)
|
self.ui.connecting(self.hostname, self.port)
|
||||||
imapobj = imaplibutil.WrappedIMAP4(self.hostname, self.port,
|
imapobj = imaplibutil.WrappedIMAP4(
|
||||||
timeout=socket.getdefaulttimeout())
|
self.hostname, self.port,
|
||||||
|
timeout=socket.getdefaulttimeout(),
|
||||||
|
use_socket=self.proxied_socket,
|
||||||
|
)
|
||||||
|
|
||||||
if not self.preauth_tunnel:
|
if not self.preauth_tunnel:
|
||||||
try:
|
try:
|
||||||
|
Loading…
Reference in New Issue
Block a user