mirror of
https://github.com/ankitects/anki.git
synced 2025-09-24 16:56:36 -04:00
bundle ssl certs; share con across all sync types
This commit is contained in:
parent
92352d4725
commit
d148a6cf1b
5 changed files with 85 additions and 20 deletions
55
anki/ankiweb.certs
Normal file
55
anki/ankiweb.certs
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIFFzCCA/+gAwIBAgIRAP+ceCiXnKf8x8mBIBmTckswDQYJKoZIhvcNAQEFBQAw
|
||||||
|
cTELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G
|
||||||
|
A1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ29tb2RvIENBIExpbWl0ZWQxFzAVBgNV
|
||||||
|
BAMTDlBvc2l0aXZlU1NMIENBMB4XDTExMDkxNTAwMDAwMFoXDTE0MDkxNDIzNTk1
|
||||||
|
OVowTzEhMB8GA1UECxMYRG9tYWluIENvbnRyb2wgVmFsaWRhdGVkMRQwEgYDVQQL
|
||||||
|
EwtQb3NpdGl2ZVNTTDEUMBIGA1UEAxMLYW5raXdlYi5uZXQwggEiMA0GCSqGSIb3
|
||||||
|
DQEBAQUAA4IBDwAwggEKAoIBAQDEDfc2WFrF5mkg4yrrbu/bxqW5SLu1qPp3Vtlv
|
||||||
|
+he8Fzkn44Zc8in7Q/t8pOxRMNq5nRjsKNUr51Zv206Z8aDzV6Mi2LtdHhADTOQW
|
||||||
|
UGa2+ANcvGJg6lCJ4WvQwcDIktO9kGgmlkMvuPAskF+nhRi7TlTaU8lhHlhMV5zu
|
||||||
|
G0XfXjPGitK1m5egIY78PJoVK+M/k44NMxi0sb+XgErXW0k6QdCZvba0z5Heks7+
|
||||||
|
aIFmLjx7bcEQxQS/+1nIK05nNrDPi7TymwZOM+b2T48t7+x9H9cjEeLUZsCiETja
|
||||||
|
SBLyj/2WLFYfH7jZuwylwcCvJ5PlnutAk7za3iASpGpUvWdFAgMBAAGjggHKMIIB
|
||||||
|
xjAfBgNVHSMEGDAWgBS4yhHpBjF528OUxugZKry7NRYxpDAdBgNVHQ4EFgQUGUTe
|
||||||
|
GYqlhmH/El7O10/hoPgIdqEwDgYDVR0PAQH/BAQDAgWgMAwGA1UdEwEB/wQCMAAw
|
||||||
|
HQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMEYGA1UdIAQ/MD0wOwYLKwYB
|
||||||
|
BAGyMQECAgcwLDAqBggrBgEFBQcCARYeaHR0cDovL3d3dy5wb3NpdGl2ZXNzbC5j
|
||||||
|
b20vQ1BTMGkGA1UdHwRiMGAwL6AtoCuGKWh0dHA6Ly9jcmwuY29tb2RvY2EuY29t
|
||||||
|
L1Bvc2l0aXZlU1NMQ0EuY3JsMC2gK6AphidodHRwOi8vY3JsLmNvbW9kby5uZXQv
|
||||||
|
UG9zaXRpdmVTU0xDQS5jcmwwawYIKwYBBQUHAQEEXzBdMDUGCCsGAQUFBzAChilo
|
||||||
|
dHRwOi8vY3J0LmNvbW9kb2NhLmNvbS9Qb3NpdGl2ZVNTTENBLmNydDAkBggrBgEF
|
||||||
|
BQcwAYYYaHR0cDovL29jc3AuY29tb2RvY2EuY29tMCcGA1UdEQQgMB6CC2Fua2l3
|
||||||
|
ZWIubmV0gg93d3cuYW5raXdlYi5uZXQwDQYJKoZIhvcNAQEFBQADggEBAI6Mcuwd
|
||||||
|
OQTvTkeZ45j2VcI1hR/nqSf2VnisxRQxNRr+n8grjt1ulqYJWJyOrocUINW7XyoJ
|
||||||
|
jHcFpS30m/E4ZedaHXq++hJqjat140r5TRcBigAHnZj7u69hjAhwG/A6Er0JFbX+
|
||||||
|
eCwe1SCBUgDLGhcNA4o3cykmgK6qG/drj5/CVwpTVKzQ65JGxEMgWehELraPzbx9
|
||||||
|
mi3e9BMSC11eEsE6O0CpBL+ENcXngYpyi1R3GYFce9oFk+ps/1yYiUstgStq4obJ
|
||||||
|
8MdDZKB8qOLFPe097FGRcOz1JRDYDVD8JQ+d+o4T3/EHuxZ2EJXhAXJDc/FoKzru
|
||||||
|
Pb1JBxNB1O0QCmE=
|
||||||
|
-----END CERTIFICATE-----
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIENjCCAx6gAwIBAgIBATANBgkqhkiG9w0BAQUFADBvMQswCQYDVQQGEwJTRTEU
|
||||||
|
MBIGA1UEChMLQWRkVHJ1c3QgQUIxJjAkBgNVBAsTHUFkZFRydXN0IEV4dGVybmFs
|
||||||
|
IFRUUCBOZXR3b3JrMSIwIAYDVQQDExlBZGRUcnVzdCBFeHRlcm5hbCBDQSBSb290
|
||||||
|
MB4XDTAwMDUzMDEwNDgzOFoXDTIwMDUzMDEwNDgzOFowbzELMAkGA1UEBhMCU0Ux
|
||||||
|
FDASBgNVBAoTC0FkZFRydXN0IEFCMSYwJAYDVQQLEx1BZGRUcnVzdCBFeHRlcm5h
|
||||||
|
bCBUVFAgTmV0d29yazEiMCAGA1UEAxMZQWRkVHJ1c3QgRXh0ZXJuYWwgQ0EgUm9v
|
||||||
|
dDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALf3GjPm8gAELTngTlvt
|
||||||
|
H7xsD821+iO2zt6bETOXpClMfZOfvUq8k+0DGuOPz+VtUFrWlymUWoCwSXrbLpX9
|
||||||
|
uMq/NzgtHj6RQa1wVsfwTz/oMp50ysiQVOnGXw94nZpAPA6sYapeFI+eh6FqUNzX
|
||||||
|
mk6vBbOmcZSccbNQYArHE504B4YCqOmoaSYYkKtMsE8jqzpPhNjfzp/haW+710LX
|
||||||
|
a0Tkx63ubUFfclpxCDezeWWkWaCUN/cALw3CknLa0Dhy2xSoRcRdKn23tNbE7qzN
|
||||||
|
E0S3ySvdQwAl+mG5aWpYIxG3pzOPVnVZ9c0p10a3CitlttNCbxWyuHv77+ldU9U0
|
||||||
|
WicCAwEAAaOB3DCB2TAdBgNVHQ4EFgQUrb2YejS0Jvf6xCZU7wO94CTLVBowCwYD
|
||||||
|
VR0PBAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wgZkGA1UdIwSBkTCBjoAUrb2YejS0
|
||||||
|
Jvf6xCZU7wO94CTLVBqhc6RxMG8xCzAJBgNVBAYTAlNFMRQwEgYDVQQKEwtBZGRU
|
||||||
|
cnVzdCBBQjEmMCQGA1UECxMdQWRkVHJ1c3QgRXh0ZXJuYWwgVFRQIE5ldHdvcmsx
|
||||||
|
IjAgBgNVBAMTGUFkZFRydXN0IEV4dGVybmFsIENBIFJvb3SCAQEwDQYJKoZIhvcN
|
||||||
|
AQEFBQADggEBALCb4IUlwtYj4g+WBpKdQZic2YR5gdkeWxQHIzZlj7DYd7usQWxH
|
||||||
|
YINRsPkyPef89iYTx4AWpb9a/IfPeHmJIZriTAcKhjW88t5RxNKWt9x+Tu5w/Rw5
|
||||||
|
6wwCURQtjr0W4MHfRnXnJK3s9EK0hZNwEGe6nQY1ShjTK3rMUUKhemPR5ruhxSvC
|
||||||
|
Nr4TDea9Y355e6cJDUCrat2PisP29owaQgVR1EX1n6diIWgVIEM8med8vSTYqZEX
|
||||||
|
c4g/VhsxOBi0cQ+azcgOno4uG+GMmIPLHzHxREzGBHNJdmAPx/i9F4BrLunMTA5a
|
||||||
|
mnkPIAou1Z5jJh5VkpTYghdae9C8x49OhgQ=
|
||||||
|
-----END CERTIFICATE-----
|
|
@ -43,6 +43,8 @@ SYNC_HOST = os.environ.get("SYNC_HOST") or "dev.ankiweb.net"
|
||||||
SYNC_PORT = int(os.environ.get("SYNC_PORT") or 80)
|
SYNC_PORT = int(os.environ.get("SYNC_PORT") or 80)
|
||||||
SYNC_URL = "http://%s:%d/sync/" % (SYNC_HOST, SYNC_PORT)
|
SYNC_URL = "http://%s:%d/sync/" % (SYNC_HOST, SYNC_PORT)
|
||||||
SYNC_VER = 0
|
SYNC_VER = 0
|
||||||
|
HTTP_CERTS = os.path.join(os.path.basename(__file__), "ankiweb.certs")
|
||||||
|
HTTP_TIMEOUT = 60
|
||||||
|
|
||||||
# deck schema
|
# deck schema
|
||||||
SCHEMA_VERSION = 1
|
SCHEMA_VERSION = 1
|
||||||
|
|
24
anki/sync.py
24
anki/sync.py
|
@ -7,7 +7,7 @@ from cStringIO import StringIO
|
||||||
from datetime import date
|
from datetime import date
|
||||||
from anki.db import DB
|
from anki.db import DB
|
||||||
from anki.errors import *
|
from anki.errors import *
|
||||||
from anki.utils import ids2str, checksum, intTime
|
from anki.utils import ids2str, checksum, intTime, httpCon
|
||||||
from anki.consts import *
|
from anki.consts import *
|
||||||
from anki.lang import _
|
from anki.lang import _
|
||||||
from hooks import runHook
|
from hooks import runHook
|
||||||
|
@ -383,7 +383,7 @@ class HttpSyncer(object):
|
||||||
|
|
||||||
# retrieving a host key for future operations
|
# retrieving a host key for future operations
|
||||||
def hostKey(self, pw):
|
def hostKey(self, pw):
|
||||||
h = httplib2.Http(timeout=60)
|
h = httpCon()
|
||||||
resp, cont = h.request(
|
resp, cont = h.request(
|
||||||
SYNC_URL+"hostKey?" + urllib.urlencode(dict(u=self.user,p=pw)))
|
SYNC_URL+"hostKey?" + urllib.urlencode(dict(u=self.user,p=pw)))
|
||||||
if resp['status'] != '200':
|
if resp['status'] != '200':
|
||||||
|
@ -452,11 +452,10 @@ class RemoteServer(Syncer, HttpSyncer):
|
||||||
def __init__(self, user, hkey):
|
def __init__(self, user, hkey):
|
||||||
self.user = user
|
self.user = user
|
||||||
self.hkey = hkey
|
self.hkey = hkey
|
||||||
self.con = None
|
self.con = httpCon()
|
||||||
|
|
||||||
def meta(self):
|
def meta(self):
|
||||||
h = httplib2.Http(timeout=60)
|
resp, cont = self.con.request(
|
||||||
resp, cont = h.request(
|
|
||||||
SYNC_URL+"meta?" + urllib.urlencode(dict(u=self.user,v=SYNC_VER)))
|
SYNC_URL+"meta?" + urllib.urlencode(dict(u=self.user,v=SYNC_VER)))
|
||||||
# fixme: convert these into easily-catchable errors
|
# fixme: convert these into easily-catchable errors
|
||||||
if resp['status'] in ('503', '504'):
|
if resp['status'] in ('503', '504'):
|
||||||
|
@ -470,7 +469,6 @@ class RemoteServer(Syncer, HttpSyncer):
|
||||||
return simplejson.loads(cont)
|
return simplejson.loads(cont)
|
||||||
|
|
||||||
def applyChanges(self, **kw):
|
def applyChanges(self, **kw):
|
||||||
self.con = httplib2.Http(timeout=60)
|
|
||||||
return self._run("applyChanges", kw)
|
return self._run("applyChanges", kw)
|
||||||
|
|
||||||
def chunk(self, **kw):
|
def chunk(self, **kw):
|
||||||
|
@ -495,16 +493,14 @@ class RemoteServer(Syncer, HttpSyncer):
|
||||||
|
|
||||||
class FullSyncer(HttpSyncer):
|
class FullSyncer(HttpSyncer):
|
||||||
|
|
||||||
def __init__(self, col, hkey):
|
def __init__(self, col, hkey, con):
|
||||||
self.col = col
|
self.col = col
|
||||||
self.hkey = hkey
|
self.hkey = hkey
|
||||||
|
self.con = con
|
||||||
def _con(self):
|
|
||||||
return httplib2.Http(timeout=60)
|
|
||||||
|
|
||||||
def download(self):
|
def download(self):
|
||||||
self.col.close()
|
self.col.close()
|
||||||
resp, cont = self._con().request(
|
resp, cont = self.con.request(
|
||||||
SYNC_URL+"download?" + urllib.urlencode(self._vars()))
|
SYNC_URL+"download?" + urllib.urlencode(self._vars()))
|
||||||
if resp['status'] != '200':
|
if resp['status'] != '200':
|
||||||
raise Exception("Invalid response code: %s" % resp['status'])
|
raise Exception("Invalid response code: %s" % resp['status'])
|
||||||
|
@ -518,7 +514,7 @@ class FullSyncer(HttpSyncer):
|
||||||
|
|
||||||
def upload(self):
|
def upload(self):
|
||||||
self.col.beforeUpload()
|
self.col.beforeUpload()
|
||||||
assert self.postData(self._con(), "upload", open(self.col.path, "rb"),
|
assert self.postData(self.con, "upload", open(self.col.path, "rb"),
|
||||||
self._vars(), comp=6) == "OK"
|
self._vars(), comp=6) == "OK"
|
||||||
|
|
||||||
# Media syncing
|
# Media syncing
|
||||||
|
@ -589,9 +585,9 @@ class MediaSyncer(object):
|
||||||
|
|
||||||
class RemoteMediaServer(MediaSyncer, HttpSyncer):
|
class RemoteMediaServer(MediaSyncer, HttpSyncer):
|
||||||
|
|
||||||
def __init__(self, hkey):
|
def __init__(self, hkey, con):
|
||||||
self.hkey = hkey
|
self.hkey = hkey
|
||||||
self.con = httplib2.Http(timeout=60)
|
self.con = con
|
||||||
|
|
||||||
def remove(self, **kw):
|
def remove(self, **kw):
|
||||||
return simplejson.loads(
|
return simplejson.loads(
|
||||||
|
|
|
@ -3,9 +3,10 @@
|
||||||
# License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
# License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
|
|
||||||
import re, os, random, time, types, math, htmlentitydefs, subprocess, \
|
import re, os, random, time, types, math, htmlentitydefs, subprocess, \
|
||||||
tempfile, shutil, string
|
tempfile, shutil, string, httplib2
|
||||||
from hashlib import sha1
|
from hashlib import sha1
|
||||||
from anki.lang import _, ngettext
|
from anki.lang import _, ngettext
|
||||||
|
from anki.consts import *
|
||||||
import locale, sys
|
import locale, sys
|
||||||
|
|
||||||
if sys.version_info[1] < 5:
|
if sys.version_info[1] < 5:
|
||||||
|
@ -297,3 +298,13 @@ def call(argv, wait=True, **kwargs):
|
||||||
|
|
||||||
isMac = sys.platform.startswith("darwin")
|
isMac = sys.platform.startswith("darwin")
|
||||||
isWin = sys.platform.startswith("win32")
|
isWin = sys.platform.startswith("win32")
|
||||||
|
|
||||||
|
# OS helpers
|
||||||
|
##############################################################################
|
||||||
|
|
||||||
|
def httpCon():
|
||||||
|
disable = os.environ.get("SSL_NOVALIDATE") or False
|
||||||
|
return httplib2.Http(
|
||||||
|
timeout=HTTP_TIMEOUT,
|
||||||
|
disable_ssl_certificate_validation=disable,
|
||||||
|
ca_certs=HTTP_CERTS)
|
||||||
|
|
|
@ -4,7 +4,7 @@ import nose, os, tempfile, shutil, time
|
||||||
from tests.shared import assertException
|
from tests.shared import assertException
|
||||||
|
|
||||||
from anki.errors import *
|
from anki.errors import *
|
||||||
from anki.utils import intTime
|
from anki.utils import intTime, httpCon
|
||||||
from anki.sync import Syncer, FullSyncer, LocalServer, RemoteServer, \
|
from anki.sync import Syncer, FullSyncer, LocalServer, RemoteServer, \
|
||||||
MediaSyncer, RemoteMediaServer
|
MediaSyncer, RemoteMediaServer
|
||||||
from anki.notes import Note
|
from anki.notes import Note
|
||||||
|
@ -65,7 +65,7 @@ def test_hkey():
|
||||||
def test_download():
|
def test_download():
|
||||||
if not TEST_REMOTE:
|
if not TEST_REMOTE:
|
||||||
return
|
return
|
||||||
f = FullSyncer(ts.client.col, "abc")
|
f = FullSyncer(ts.client.col, "abc", ts.server.con)
|
||||||
assertException(Exception, f.download)
|
assertException(Exception, f.download)
|
||||||
f.hkey = TEST_HKEY
|
f.hkey = TEST_HKEY
|
||||||
f.download()
|
f.download()
|
||||||
|
@ -77,7 +77,7 @@ def test_remoteSync():
|
||||||
# not yet associated, so will require a full sync
|
# not yet associated, so will require a full sync
|
||||||
assert ts.client.sync() == "fullSync"
|
assert ts.client.sync() == "fullSync"
|
||||||
# upload
|
# upload
|
||||||
f = FullSyncer(ts.client.col, TEST_HKEY)
|
f = FullSyncer(ts.client.col, TEST_HKEY, ts.server.con)
|
||||||
f.upload()
|
f.upload()
|
||||||
ts.client.col.reopen()
|
ts.client.col.reopen()
|
||||||
# should report no changes
|
# should report no changes
|
||||||
|
@ -89,7 +89,7 @@ def test_remoteSync():
|
||||||
assert ts.client.sync() == "noChanges"
|
assert ts.client.sync() == "noChanges"
|
||||||
# downloading the remote col should give us the same mod
|
# downloading the remote col should give us the same mod
|
||||||
lmod = ts.client.col.mod
|
lmod = ts.client.col.mod
|
||||||
f = FullSyncer(ts.client.col, TEST_HKEY)
|
f = FullSyncer(ts.client.col, TEST_HKEY, ts.server.con)
|
||||||
f.download()
|
f.download()
|
||||||
d = aopen(ts.client.col.path)
|
d = aopen(ts.client.col.path)
|
||||||
assert d.mod == lmod
|
assert d.mod == lmod
|
||||||
|
@ -101,7 +101,8 @@ def test_remoteSync():
|
||||||
|
|
||||||
def setup_remoteMedia():
|
def setup_remoteMedia():
|
||||||
setup_basic()
|
setup_basic()
|
||||||
ts.server = RemoteMediaServer(TEST_HKEY)
|
con = httpCon()
|
||||||
|
ts.server = RemoteMediaServer(TEST_HKEY, con)
|
||||||
ts.server2 = RemoteServer(TEST_USER, TEST_HKEY)
|
ts.server2 = RemoteServer(TEST_USER, TEST_HKEY)
|
||||||
ts.client = MediaSyncer(ts.deck1, ts.server)
|
ts.client = MediaSyncer(ts.deck1, ts.server)
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue