def getShellfireCerts(product_id, vpn_provider, country): # If the certs already exist, then just return # This is assuming that all of the certs remain the same, and are good for all connections for an account account_id = getAccountID() ca_name = getAddonPath(True, vpn_provider + "/" + "ca.crt") cert_name = getAddonPath(True, vpn_provider + "/" + "sf" + account_id + ".crt") key_name = getAddonPath(True, vpn_provider + "/" + "sf" + account_id + ".key") if xbmcvfs.exists(ca_name) and xbmcvfs.exists( cert_name) and xbmcvfs.exists(key_name): return True # Get the set of certificates that ar needed to connect rc, api_data = sendAPI("?action=getCertificates", "Retrieving certificates", '{"productId": "' + product_id + '"}', True) if not rc: return False # Write all of the certs to a file for item in api_data["data"]: cert_name = item["name"] if not writeCert(vpn_provider, cert_name, item["content"]): return False return True
def getShellfireOvpn(product_id, vpn_provider, country): # Retrieve the ovpn parameters and make them into an ovpn file rc, api_data = sendAPI("?action=getOpenVpnParams", "Retrieving openvpn params", '{"productId": "' + product_id + '"}', True) if not rc: return False # Parse the parameters and override some of them params = api_data["data"]["params"].split("--") filename = getAddonPath(True, vpn_provider + "/" + country + ".ovpn") output = open(filename, 'w') country_cert = "sf" + getAccountID() for p in params: p = p.strip(" \n") if p.startswith("service "): p = "" if p.startswith("ca "): p = "ca " + getAddonPathWrapper(vpn_provider + "/ca.crt") if p.startswith("cert "): p = "cert " + getAddonPathWrapper(vpn_provider + "/" + country_cert + ".crt") if p.startswith("key "): p = "key " + getAddonPathWrapper(vpn_provider + "/" + country_cert + ".key") if not p == "": output.write(p + "\n") output.close() return True
def getNordVPNLocationsCommon(vpn_provider, exclude_used, friendly): # Return a list of all of the locations or location .ovpn files addon = xbmcaddon.Addon(getID()) # Get the list of used, validated location file names used = [] if exclude_used: # Adjust the 11 below to change conn_max for i in range(1, 11): s = addon.getSetting(str(i) + "_vpn_validated_friendly") if not s == "": used.append(s) filename = getAddonPath(True, vpn_provider + "/" + NORD_LOCATIONS) # If the list of countries doesn't exist (this can happen after a reinstall) # then go and do the pre-fetch first. Otherwise this shouldn't be necessary try: if not xbmcvfs.exists(filename): getNordVPNPreFetch(vpn_provider) except Exception as e: errorTrace( "alternativeNord.py", "Couldn't download the list of countries for " + vpn_provider + " from " + filename) errorTrace("alternativeNord.py", str(e)) return [] # Read the locations file and generate the location file name, excluding any that are used try: locations_file = open(filename, 'r') locations = locations_file.readlines() locations_file.close() return_locations = [] for l in locations: country, id = l.split(",") if not exclude_used or not country in used: if friendly: return_locations.append(country) else: return_locations.append( getNordVPNLocationName(vpn_provider, country)) return return_locations except Exception as e: errorTrace( "alternativeNord.py", "Couldn't read the list of countries for " + vpn_provider + " from " + filename) errorTrace("alternativeNord.py", str(e)) return []
def writeCert(vpn_provider, cert_name, content): # Write out the certificate represented by the content filename = getAddonPath(True, vpn_provider + "/" + cert_name) try: line = "" debugTrace("Writing certificate " + cert_name) output = open(filename, 'w') # Output the content line by line for line in content: output.write(line) output.close() return True except Exception as e: errorTrace("alternativeShellfire`.py", "Couldn't write certificate " + filename) errorTrace("alternativeShellfire.py", str(e)) return False
def getNordVPNLocation(vpn_provider, location, server_count, just_name): # Return friendly name and .ovpn file name # Given the location, find the country ID of the servers addon = xbmcaddon.Addon(getID()) filename = getAddonPath(True, vpn_provider + "/" + NORD_LOCATIONS) # If the list of countries doesn't exist (this can happen after a reinstall) # then go and do the pre-fetch first. Otherwise this shouldn't be necessary try: if not xbmcvfs.exists(filename): getNordVPNPreFetch(vpn_provider) except Exception as e: errorTrace( "alternativeNord.py", "Couldn't download the list of countries to get ID for " + vpn_provider + " from " + filename) errorTrace("alternativeNord.py", str(e)) return "", "", "", False try: locations_file = open(filename, 'r') locations = locations_file.readlines() locations_file.close() id = "" for l in locations: country, id = l.split(",") id = id.strip(' \t\n\r') if location == country: break if id == "": errorTrace( "alternativeNord.py", "Couldn't retrieve location " + location + " for " + vpn_provider + " from " + filename) return "", "", "", False except Exception as e: errorTrace( "alternativeNord.py", "Couldn't read the list of countries to get ID for " + vpn_provider + " from " + filename) errorTrace("alternativeNord.py", str(e)) return "", "", "", False # Generate the file name from the location location_filename = getNordVPNLocationName(vpn_provider, location) if just_name: return location, location_filename, "", False # Download the JSON object of servers response = "" error = True try: if "UDP" in addon.getSetting("vpn_protocol"): protocol = "udp" else: protocol = "tcp" download_url = "https://api.nordvpn.com/v1/servers/recommendations?filters[servers_technologies][identifier]=openvpn_" + protocol + "&filters[country_id]=" + id + "&filters[servers_groups][identifier]=legacy_standard" if ifHTTPTrace(): infoTrace( "alternativeNord.py", "Downloading server info for " + location + " with ID " + id + " and protocol " + protocol + " using " + download_url) else: debugTrace("Downloading server info for " + location + " with ID " + id + " and protocol " + protocol) token = getTokenNordVPN() req = urllib2.Request(download_url) req.add_header("Authorization", "token:" + token) t_before = now() response = urllib2.urlopen(req, timeout=10) server_data = json.load(response) t_after = now() response.close() error = False if ifJSONTrace(): infoTrace( "alternativeNord.py", "JSON received is \n" + json.dumps(server_data, indent=4)) if t_after - t_before > TIME_WARN: infoTrace( "alternativeNord.py", "Downloading server info for " + location + " with ID " + id + " and protocol " + protocol + " took " + str(t_after - t_before) + " seconds") except urllib2.HTTPError as e: errorTrace( "alternativeNord.py", "Couldn't retrieve the server info for " + vpn_provider + " location " + location + ", ID " + id) errorTrace("alternativeNord.py", "API call was " + download_url) errorTrace("alternativeNord.py", "Response was " + str(e.code) + " " + e.reason) errorTrace("alternativeNord.py", e.read()) except Exception as e: errorTrace( "alternativeNord.py", "Couldn't retrieve the server info for " + vpn_provider + " location " + location + ", ID " + id) errorTrace("alternativeNord.py", "API call was " + download_url) errorTrace("alternativeNord.py", "Response was " + str(type(e)) + " " + str(e)) if error: # If there's an API connectivity issue but a location file exists then use that # Won't have the latest best location in it though if xbmcvfs.exists(location_filename): infoTrace("alternativeNord.py", "Using existing " + location + " file") return location, location_filename, "", False else: return "", "", "", False # First server is the best one, but if it didn't connect last time then skip it. The last attempted server # will be cleared on a restart, or a successful connection. If there are no more connections to try, then # it will try the first connection again. However, if this is > 4th attempt to connect outside of the # validation then it'll always default to the best as it's likely there's a network rather than server problem last = getVPNRequestedServer() if not last == "" and server_count < 5: debugTrace( "Server " + last + " didn't connect last time so will be skipping to the next server." ) last_found = False else: last = "" last_found = True first_server = "" for item in server_data: name = item["name"] server = item["hostname"] status = item["status"] load = str(item["load"]) #debugTrace("Next is " + name + ", " + server + ", " + status + ". Load is " + load) if status == "online": if first_server == "": first_server = server if last_found: debugTrace("Using " + name + ", " + server + ", online. Load is " + load) break if server == last: last_found = True server = "" if server == "": server = first_server setVPNRequestedServer(server) setVPNURL(server) # Fetch the ovpn file for the server if not server == "": if not getNordVPNOvpnFile(server, protocol, location_filename): if not xbmcvfs.exists(location_filename): return "", "", "", False return location, location_filename, "", False else: return "", "", "", False
def getNordVPNLocationName(vpn_provider, location): return getAddonPath(True, vpn_provider + "/" + location + ".ovpn")
def getNordVPNPreFetch(vpn_provider): # Fetch and store country info from the magical interwebz filename = getAddonPath(True, vpn_provider + "/" + NORD_LOCATIONS) if xbmcvfs.exists(filename): try: st = xbmcvfs.Stat(filename) create_time = int(st.st_ctime()) t = now() # Fetch again if this is more than a day old otherwise use what there is if create_time + 86400 < t: debugTrace("Create time of " + filename + " is " + str(create_time) + " time now is " + str(t) + ", fetching country data again") else: debugTrace("Create time of " + filename + " is " + str(create_time) + " time now is " + str(t) + ", using existing data") return True except Exception as e: errorTrace( "alternativeNord.py", "List of countries exist but couldn't get the time stamp for " + filename) errorTrace("alternativeNord.py", str(e)) return False # Download the JSON object of countries response = "" error = True try: download_url = "https://api.nordvpn.com/v1/servers/countries" if ifHTTPTrace(): infoTrace("alternativeNord.py", "Downloading list of countries using " + download_url) else: debugTrace("Downloading list of countries") token = getTokenNordVPN() req = urllib2.Request(download_url) req.add_header("Authorization", "token:" + token) t_before = now() response = urllib2.urlopen(req, timeout=10) country_data = json.load(response) t_after = now() response.close() error = False if ifJSONTrace(): infoTrace( "alternativeNord.py", "JSON received is \n" + json.dumps(country_data, indent=4)) if t_after - t_before > TIME_WARN: infoTrace( "alternativeNord.py", "Downloading list of countries took " + str(t_after - t_before) + " seconds") except urllib2.HTTPError as e: errorTrace( "alternativeNord.py", "Couldn't retrieve the list of countries for " + vpn_provider) errorTrace("alternativeNord.py", "API call was " + download_url) errorTrace("alternativeNord.py", "Response was " + str(e.code) + " " + e.reason) errorTrace("alternativeNord.py", e.read()) except Exception as e: errorTrace( "alternativeNord.py", "Couldn't retrieve the list of countries for " + vpn_provider) errorTrace("alternativeNord.py", "API call was " + download_url) errorTrace("alternativeNord.py", "Response was " + str(type(e)) + " " + str(e)) if error: # Use the existing list of countries if there is one as it'll be pretty much up to date if xbmcvfs.exists(filename): infoTrace("alternativeNord.py", "Using existing list of countries") return True else: return False # Parse the JSON to write out the countries and ID try: debugTrace("Parsing the JSON and writing the list of countries") output = open(filename, 'w') for item in country_data: name = item["name"].replace(",", "") output.write(name + "," + str(item["id"]) + "\n") output.close() return True except Exception as e: errorTrace( "alternativeNord.py", "Couldn't write the list of countries for " + vpn_provider + " to " + filename) errorTrace("alternativeNord.py", str(e)) # Delete the country file if the was a problem creating it. This will force a download next time through try: if xbmcvfs.exists(filename): errorTrace( "alternativeNord.py", "Deleting country file " + filename + " to clean up after previous error") xbmcvfs.delete(filename) except Exception as e: errorTrace("alternativeNord.py", "Couldn't delete the country file " + filename) errorTrace("alternativeNord.py", str(e)) return False
def getAddonPathWrapper(name): # Return the fully qualified add-on path and file name if getPlatform() == platforms.WINDOWS: return getAddonPath(True, name).replace("\\", "\\\\") else: return getAddonPath(True, name)
def getShellfireLocation(vpn_provider, location, server_count, just_name): # Return the friendly and .ovpn name addon = xbmcaddon.Addon(getID()) # Just return if this is a title that's been passed in if location.startswith(TITLE_START): return "", "", "Select a location or server", True # Remove all of the tagging # There's some escaping of the UPGRADE_END characters when passed in via the add-on menu # This is why the command below searches for the end of the upgrade and strips it location = location.replace(UPGRADE_START, "") if "I]" in location: location = location[:(location.index("I]") - 2)] location = location.strip(" ") filename = getAddonPath(True, vpn_provider + "/" + SHELLFIRE_LOCATIONS) try: if not xbmcvfs.exists(filename): getShellfirePreFetch(vpn_provider) except Exception as e: errorTrace( "alternativeShellfire.py", "Couldn't download the list of locations for " + vpn_provider + " from " + filename) errorTrace("alternativeShellfire.py", str(e)) return "", "", "", False try: # Read the locations from the file and list by account type locations_file = open(filename, 'r') locations = locations_file.readlines() locations_file.close() for l in locations: if location in l: country, server, type, server_id = l.split(",") server_id = server_id.strip(" \n") break # Return an upgrade message if this server is not available to the user if ACCOUNT_TYPES.index(type) > ACCOUNT_TYPES.index(getAccountType()): _, message = getShellfireMessages(vpn_provider, 0, "") if message == "": message = "Get access to servers in over 30 countries with unlimited speed at shellfire.net/kodi" return "", "", "Upgrade to use this [B]" + type + "[/B] location.\n" + message, False # Generate the file name from the location location_filename = getShellfireLocationName(vpn_provider, country) if just_name: return location, location_filename, "", False except Exception as e: errorTrace( "alternativeShellfire.py", "Couldn't read the list of locations for " + vpn_provider + " from " + filename) errorTrace("alternativeShellfire.py", str(e)) return "", "", "", False # Set the selected server for the VPN being used try: setShellfireServer(getAccountID(), server_id) # Set the protocol. If it's "UDP and TCP", choose UDP proto = addon.getSetting("vpn_protocol") if "UDP" in proto: proto = "UDP" if not setShellfireProtocol(getAccountID(), proto): raise Exception("Couldn't set the protocol") # Get the parameters associated with this server and put them in a file if not getShellfireOvpn(getAccountID(), vpn_provider, country): raise Exception("Couldn't create an OVPN file") # Get the certs associated with this server and put them in a file if not getShellfireCerts(getAccountID(), vpn_provider, country): raise Exception("Couldn't create the certificates") return country, location_filename, "", False except Exception as e: errorTrace( "alternativeShellfire.py", "Couldn't read the list of locations for " + vpn_provider + " from " + filename) errorTrace("alternativeShellfire.py", str(e)) return "", "", "", False
def getShellfireLocationName(vpn_provider, location): # Return the ovpn file name return getAddonPath(True, vpn_provider + "/" + location + ".ovpn")
def getShellfireLocationsCommon(vpn_provider, exclude_used, friendly, servers): # Return a list of all of the locations addon = xbmcaddon.Addon(getID()) # Get the list of used, validated location file names used = [] if exclude_used: # Adjust the 11 below to change conn_max for i in range(1, 11): s = addon.getSetting(str(i) + "_vpn_validated_friendly") if not s == "": used.append(s) filename = getAddonPath(True, vpn_provider + "/" + SHELLFIRE_LOCATIONS) # If the list of locations doesn't exist (this can happen after a reinstall) # then go and do the pre-fetch first. Otherwise this shouldn't be necessary try: if not xbmcvfs.exists(filename): getShellfirePreFetch(vpn_provider) except Exception as e: errorTrace( "alternativeShellfire.py", "Couldn't download the list of locations for " + vpn_provider + " from " + filename) errorTrace("alternativeShellfire.py", str(e)) return [] try: service = ACCOUNT_TYPES.index(getAccountType()) except Exception as e: errorTrace("alternativeShellfire.py", "Don't have an account for " + vpn_provider) errorTrace("alternativeShellfire.py", str(e)) return [] try: # Read the locations from the file and list by account type locations_file = open(filename, 'r') locations = locations_file.readlines() locations_file.close() return_locations = [] # List the free servers return_locations.append(TITLE_START + "Free Locations" + TITLE_END) for l in locations: country, server, type, server_id = l.split(",") server_id = server_id.strip(" \n") if type == ACCOUNT_TYPES[0]: if not exclude_used or not country in used: if friendly: return_locations.append(SERVER_START + country + SERVER_END) elif servers: return_locations.append(SERVER_START + server + SERVER_END) else: return_locations.append( type + getShellfireLocationName(vpn_provider, country)) # List the paid servers return_locations.append(TITLE_START + "Paid Locations" + TITLE_END) for l in locations: country, server, type, server_id = l.split(",") server_id = server_id.strip(" \n") if not type == ACCOUNT_TYPES[0]: if ACCOUNT_TYPES.index(type) > service: start = UPGRADE_START end = UPGRADE_END else: start = SERVER_START end = SERVER_END if not exclude_used or not country in used: if friendly: return_locations.append(start + country + end) elif servers: return_locations.append(start + server + end) else: return_locations.append( type + getShellfireLocationName(vpn_provider, country)) return return_locations except Exception as e: errorTrace( "alternativeShellfire.py", "Couldn't read the list of locations for " + vpn_provider + " from " + filename) errorTrace("alternativeShellfire.py", str(e)) return [] return []
def getShellfirePreFetch(vpn_provider): # Fetch and store location info filename = getAddonPath(True, vpn_provider + "/" + SHELLFIRE_LOCATIONS) if xbmcvfs.exists(filename): try: st = xbmcvfs.Stat(filename) create_time = int(st.st_ctime()) t = now() # Fetch again if this is more than a day old otherwise use what there is if create_time + 86400 < t: debugTrace("Create time of " + filename + " is " + str(create_time) + " time now is " + str(t) + ", fetching location data again") else: debugTrace("Create time of " + filename + " is " + str(create_time) + " time now is " + str(t) + ", using existing data") # Less than a day old, so using the existing file return True except Exception as e: errorTrace( "alternativeShellfire.py", "List of countries exist but couldn't get the time stamp for " + filename) errorTrace("alternativeShellfire.py", str(e)) return False # Download the list of locations error = True try: response = "" api_data = "" rest_url = "https://www.shellfire.de/webservice/serverlist.php" if ifHTTPTrace(): infoTrace("alternativeShellfire.py", "Downloading list of locations using " + rest_url) else: debugTrace("Downloading list of locations") # This is not a JSON call, a header and servers are returned in a ; separated list req = urllib2.Request(rest_url, "", REQUEST_HEADERS) t_before = now() response = urllib2.urlopen(req) api_data = response.read() t_after = now() response.close() if ifJSONTrace(): infoTrace("alternativeShellfire.py", "Text received is \n" + api_data) if t_after - t_before > TIME_WARN: infoTrace( "alternativeShellfire.py", "Retrieving list of locations took " + str(t_after - t_before) + " seconds") except urllib2.HTTPError as e: errorTrace("alternativeShellfire.py", "Couldn't retrieve the list of locations") errorTrace("alternativeShellfire.py", "API call was " + rest_url) if not api_data == "": errorTrace("alternativeShellfire.py", "Data returned was \n" + api_data) errorTrace("alternativeShellfire.py", "Response was " + str(e.code) + " " + e.reason) errorTrace("alternativeShellfire.py", e.read()) return False except Exception as e: errorTrace("alternativeShellfire.py", "Couldn't retrieve the list of locations") errorTrace("alternativeShellfire.py", "API call was " + rest_url) if not api_data == "": errorTrace("alternativeShellfire.py", "Data returned was \n" + api_data) errorTrace("alternativeShellfire.py", "Response was " + str(type(e)) + " " + str(e)) return False # The first line has the headers, so find the position of the information that's interesting api_table = api_data.split("\n") headers = api_table[0].split(";") id_pos = headers.index("iVpnServerId") country_pos = headers.index("Country") city_pos = headers.index("sCity") host_pos = headers.index("sHost") type_pos = headers.index("eServerType") debugTrace("Header decoded. ID is " + str(id_pos) + ", Country is " + str(country_pos) + ", City is " + str(city_pos) + ", Host is " + str(host_pos) + ", Type is " + str(type_pos)) api_table[0] = "" try: line = "" cleaned_data = [] debugTrace( "Parsing the text and extracting the country, server and type") for line in api_table: server_data = line.split(";") # Avoid parsing empty lines, or lines where there's not enough data if len(server_data) > 5: cleaned_data.append(server_data[country_pos] + " - " + server_data[city_pos] + " (S" + server_data[id_pos] + ")," + server_data[host_pos] + "," + server_data[type_pos] + "," + server_data[id_pos] + "\n") except Exception as e: errorTrace("alternativeShellfire`.py", "Couldn't parse the list of locations for " + vpn_provider) if not server_data == "": errorTrace("alternativeShellfire.py", "Processing line " + line) errorTrace("alternativeShellfire.py", str(e)) return False # Sort the locations alphabetically cleaned_data.sort() try: line = "" debugTrace("Parsing the text and writing the list of locations") output = open(filename, 'w') # Parse the data and create list containing the stuff we care about for line in cleaned_data: output.write(line) output.close() return True except Exception as e: errorTrace( "alternativeShellfire`.py", "Couldn't write the list of locations for " + vpn_provider + " to " + filename) if not server_data == "": errorTrace("alternativeShellfire.py", "Processing server " + line) errorTrace("alternativeShellfire.py", str(e)) # Delete the location file if the was a problem creating it. This will force a download next time through try: if xbmcvfs.exists(filename): errorTrace( "alternativeShellfire.py", "Deleting location file " + filename + " to clean up after previous error") xbmcvfs.delete(filename) except Exception as e: errorTrace("alternativeShellfire.py", "Couldn't delete the location file " + filename) errorTrace("alternativeShellfire.py", str(e)) return False
def onInit(self): self.getControl(400).setImage(getAddonPath(True, "/resources/map.png")) self.getControl(401).addLabel( xbmcaddon.Addon(getID()).getAddonInfo("name")) self.getControl(402).addLabel("Press a key to map or wait to clear.")
n = "Yes" else: msg = "Key ID " + cycle_key + " is mapped to the VPN cycle function. Remap or clear current mapping?" y = "Clear" n = "Remap" if not xbmcgui.Dialog().yesno( addon_name, msg, nolabel=n, yeslabel=y): cycle_key = KeyListener().record_key() if cycle_key == "": dialog = "VPN cycle is not mapped to a key." icon = "/resources/unmapped.png" else: dialog = "VPN cycle is mapped to key ID " + cycle_key + "." icon = "/resources/mapped.png" xbmcgui.Dialog().notification(addon_name, dialog, getAddonPath(True, icon), 5000, False) else: if not cycle_key == "": cycle_key = "" if action == "table": if table_key == "": msg = "Do you want to map a key or remote button to the VPN connection table function?" y = "No" n = "Yes" else: msg = "Key ID " + table_key + " is mapped to the VPN connection table function. Remap or clear current mapping?" y = "Clear" n = "Remap" if not xbmcgui.Dialog().yesno(