# (c) Copyright 2009-2012, 2015. CodeWeavers, Inc.
import os
import stat
import cxlog
import distversion
import urllib2
import cxobjc
import binascii
import tempfile
import proxyinfo
import cxutils
import cxconfig
# for localization
from cxutils import cxgettext as _
class DemoUtils(cxobjc.Proxy):
pass
@cxobjc.method(DemoUtils, 'demoStatusForLicenseFile_andSig_')
def demo_status(licenseFile, sigFile):
"""Checks the demo-status of the install. Returns a 5-tuple.
The first item is a simple boolean: True if we're in demo
mode, False if we aren't. The second item is a string which
will describe the account used to register the demo, if any.
The third entry is the expiration date of the license.
Fourth entry: License ID
Fifth entry: True if this license has been revoked
"""
keyFile = os.path.join(cxutils.CX_ROOT, "share/crossover/data", "tie.pub")
if not (os.path.exists(licenseFile) and os.path.exists(sigFile)):
cxlog.log("Unable to locate license %s and/or signature %s.\n" % (licenseFile, sigFile))
return (True, None, None, None, False)
if not signatureIsValid(licenseFile, sigFile, keyFile):
cxlog.log("Signature %s for file %s is invalid.\n" % (sigFile, licenseFile))
return (True, None, None, None, False)
licenseData = cxconfig.get(licenseFile)
username = licenseData[distversion.DEMO_SKU].get('customer')
expiration = licenseData[distversion.DEMO_SKU].get('expires')
licenseID = licenseData["license"].get('id')
if not expiration or not username or not licenseID:
# The license file is there, but there's no entry for this product.
return (True, None, None, None, False)
import c4profilesmanager
if licenseID in c4profilesmanager.C4ProfilesSet.get_revoked_licenses():
return (True, username, expiration, licenseID, True)
builddate = distversion.DATE
expirationdate = expiration
if builddate > expirationdate:
return (True, username, expiration, licenseID, False)
return (False, username, expiration, licenseID, False)
@cxobjc.method(DemoUtils, 'canRegisterWithLicenseDir_')
def can_register_as_current_user(licenseDir):
""" Returns True if we have current permission to register. Return
False if we need to authenticate as root before registering.
"""
licenseFile = os.path.join(licenseDir, "license.txt")
sigFile = os.path.join(licenseDir, "license.sig")
if os.path.exists(licenseFile):
if not os.access(licenseFile, os.W_OK):
return False
if os.path.exists(sigFile):
if not os.access(sigFile, os.W_OK):
return False
if os.path.exists(licenseDir):
return os.access(licenseDir, os.W_OK)
parentDir = os.path.dirname(licenseDir)
return os.access(parentDir, os.W_OK)
@cxobjc.method(DemoUtils, 'serialRegistrationURL')
def serial_reg_url():
return "https://www.codeweavers.com/bin/serial"
@cxobjc.method(DemoUtils, 'licenseUrlForLogin_andPassword_')
def reg_license_url(login, password):
# Get an md5 of the password.
md5hasher = cxutils.md5_hasher()
md5hasher.update(password)
mdPassword = md5hasher.hexdigest()
URL = "HTTPS://www.codeweavers.com/bin/register/%s;%s;lic;%s;md5;0;bom" % (login, mdPassword, distversion.DEMO_EXTREP)
return URL
def json_format_start():
# Python 2.6 and higher have a json library, but we still
# target earlier versions, and it's trivial to encode
return "{"
def json_format(key, value):
return "\"%s\":\"%s\"," % (key, value)
def json_format_end(key, value):
return "\"%s\":\"%s\"}" % (key, value)
@cxobjc.method(DemoUtils, 'createAccountRequestBodyForEmail_name_andPassword_withSerialCode_')
def create_account_request_body_for_email_name_password_with_serial_code(email, name, password, serial):
# create json request asking the server to create a new account
request = json_format_start()
request += json_format("ver", "1")
request += json_format("cxver", distversion.CX_VERSION)
request += json_format("name", name)
request += json_format("email", email)
request += json_format("password", password)
request += json_format("sub", "0")
request += json_format_end("serial", serial)
return request
@cxobjc.method(DemoUtils, 'checkSerialCodeRegistrationResponse_')
def checkSerialCodeRegistrationResponse(responseBuffer):
if responseBuffer.find("OK", 0, 16) != -1:
errorString = None
elif responseBuffer.find("UNKNOWNSERIAL", 0, 16) != -1:
errorString = _("CrossOver does not recognize this activation code.\n\nBe sure you have entered the activation code correctly.\n\n") + lastResort()
elif responseBuffer.find("WRONGPASSWORD", 0, 16) != -1:
errorString = _("An account already exists for the email address which you have entered, but you have not provided the correct password.\n\nIf you already have an account with codeweavers.com, please enter the current password for your account.") + " " + lastResort()
elif responseBuffer.find("BADEMAIL", 0, 16) != -1:
errorString = _("The email address you have entered is not in a valid format. Please enter a valid email address.\n\n") + lastResort()
elif responseBuffer.find("SERIALREGISTERED", 0, 16) != -1:
errorString = _("The activation code you have entered has already been registered, and cannot be used again.") + " " + lastResortErroneous()
elif responseBuffer.find("EXPIRED", 0, 16) != -1:
errorString = _("The activation code you have entered has expired and can no longer be used.") + " " + lastResortErroneous()
elif responseBuffer.find("ERROR", 0, 16) != -1:
errorString = _("The server has encountered an unknown error trying to create your account or register your activation code.\n\n") + lastResort() + "\n"
else:
errorString = _("CrossOver has received an unrecognized response from the server while trying to create your account and register your activation code.\n\n") + lastResort() + "\n"
return errorString
@cxobjc.method(DemoUtils, 'splitLicenseAndSigFromBuffer_')
def splitLicenseAndSigFromBuffer(licenseBuffer_data):
licenseBuffer = str(licenseBuffer_data).decode("utf_8")
[openLicenseFD, licenseTempFile] = tempfile.mkstemp(prefix="license.txt")
os.chmod(licenseTempFile,
stat.S_IRUSR | stat.S_IWUSR |
stat.S_IRGRP |
stat.S_IROTH)
openLicense = os.fdopen(openLicenseFD, "w")
[openSigFD, sigTempFile] = tempfile.mkstemp(prefix="license.sig")
os.chmod(sigTempFile,
stat.S_IRUSR | stat.S_IWUSR |
stat.S_IRGRP |
stat.S_IROTH)
openSig = os.fdopen(openSigFD, "w")
rVals = (licenseTempFile, sigTempFile)
sigannounce = "<!-- Signature"
inSig = False
# Split the downloaded file into license and signature, and write them out.
for l in licenseBuffer.split(os.linesep):
if sigannounce in l:
inSig = True
if inSig:
# We have a signature embedded in an XML comment.
# Strip out the commenty bits.
strippedline = l.replace(sigannounce, "")
strippedline = strippedline.replace("-->", "")
strippedline = strippedline.rstrip()
splitline = strippedline.split("= ")
if len(splitline) > 1:
hexline = splitline[1].strip()
else:
hexline = strippedline.strip()
openSig.write(binascii.unhexlify(hexline))
else:
openLicense.write(l)
openLicense.write("\n")
openLicense.close()
openSig.close()
if not inSig:
cxlog.log("The license file we downloaded has no signature, so is probably broken.")
os.remove(rVals[0])
os.remove(rVals[1])
return (None, None)
return rVals
@cxobjc.method(DemoUtils, 'lastResort')
def lastResort():
lastResortStr = _("If this problem persists, contact CodeWeavers sales support at sales@codeweavers.com.")
return lastResortStr
def lastResortErroneous():
lastResortStr = _("If you believe this is in error, contact CodeWeavers sales support at sales@codeweavers.com.")
return lastResortStr
@cxobjc.method(DemoUtils, 'checkLicenseDownload_')
def checkLicenseDownload(licenseBuffer_data):
licenseBuffer = str(licenseBuffer_data).decode("utf_8")
errorString = None
if licenseBuffer.find("FILE NOT FOUND", 0, 16) != -1:
errorString = _("Unable to locate a key for %(product)s.\n\n") % {'product': distversion.PRODUCT_NAME}
errorString += lastResort()
elif licenseBuffer.find("USER EXPIRED", 0, 16) != -1:
errorString = _("Your support period has expired. You are no longer entitled to product upgrades.\n\nVisit store.codeweavers.com to extend your support contract. If you believe this is in error, contact CodeWeavers sales support at sales@codeweavers.com.")
elif licenseBuffer.find("USER NOT FOUND", 0, 16) != -1 or licenseBuffer.find("BAD PASSWORD", 0, 256) != -1:
errorString = _("Unable to validate your username and password.\n\nVisit store.codeweavers.com to make sure you have a valid login, and try again.\n\n") + lastResort()
elif licenseBuffer.find("ERROR", 0, 16) != -1:
errorString = _("An unknown error occurred while downloading your license file.\n\n") + lastResort()
return errorString
def create_acct_apply_scode(login, password, username, serialcode):
errorString = None
request_body = create_account_request_body_for_email_name_password_with_serial_code(login, username, password, serialcode)
URL = serial_reg_url()
try:
proxyinfo.install_default_opener()
# Note: this is duplicated from cxurlget.py
# demoutils is used during registration, and
# even something like a module import can
# break registration in hard-to-predict ways.
# So, atm, I'm just going to duplicate this.
user_agent = 'Mozilla/5.0 (like Gecko) CrossOver'
header = {'User-Agent': user_agent}
request = urllib2.Request(URL, request_body, header)
response = urllib2.urlopen(request)
responseBuffer = response.read()
except urllib2.URLError:
# Errors in communication with the server
errorString = _("Registration failed")
errorString += "\n\n" + lastResort()
cxlog.warn("registration failed: %s" % errorString)
if errorString is None:
# Errors returned by the server
errorString = checkSerialCodeRegistrationResponse(responseBuffer)
return errorString
def download_and_split_license_file(login, password):
""" Attempt to download a license file and drop it into the appropriate dir.
Returns licensefile, sigfile, errorstring where filename will be None
if the download failed. Otherwise licensefile and sigfile are temporary
files; it is the caller's responsibility to test, move, and/or delete
them.
"""
errorString = None
licenseFile = None
sigFile = None
URL = reg_license_url(login, password)
try:
proxyinfo.install_default_opener()
licenseFileLike = urllib2.urlopen(URL)
licenseBuffer = licenseFileLike.read()
except urllib2.URLError:
errorString = _("Download failed.\n\n") + lastResort()
if errorString is None:
errorString = checkLicenseDownload(licenseBuffer)
if errorString is None:
(licenseFile, sigFile) = splitLicenseAndSigFromBuffer(licenseBuffer)
if not licenseFile or not sigFile:
errorString = _("The licence file download is corrupt.\n\n") + lastResort()
licenseFile = None
sigFile = None
return licenseFile, sigFile, errorString
def signatureIsValid(fileName, sigFileName, keyFileName):
args = ("openssl", "dgst", "-sha1", "-verify", keyFileName, "-signature", sigFileName, fileName)
retcode, _out, _err = cxutils.run(args, stdout=cxutils.NULL, stderr=cxutils.NULL)
return retcode == 0