def sendAPI(command, command_text, api_data, check_response): # Common routine to send an API command try: response = "" rc = True rest_url = REQUEST_URL + command auth_token,_,_,_ = getTokens() # Login again if the token is blank and the command is not login anyway if auth_token == "" and not "=login" in command: debugTrace("Logging in again because auth token not valid") addon = xbmcaddon.Addon(getID()) rc = authenticateShellfire(addon.getSetting("vpn_provider"), addon.getSetting("vpn_username"), addon.getSetting("vpn_password")) auth_token,_,_,_ = getTokens() if auth_token == "" or not rc: raise Exception(command_text + " was not authorized") if ifHTTPTrace(): infoTrace("alternativeShellfire.py", command_text + " " + rest_url) else: debugTrace(command_text) req = urllib2.Request(rest_url, api_data, REQUEST_HEADERS) if not auth_token == "": req.add_header("x-authorization-token", auth_token) t_before = now() response = urllib2.urlopen(req) api_data = json.load(response) t_after = now() response.close() # Trace if the command took a long time if ifJSONTrace(): infoTrace("alternativeShellfire.py", "JSON received is \n" + json.dumps(api_data, indent=4)) if t_after - t_before > TIME_WARN: infoTrace("alternativeShellfire.py", command_text + " took " + str(t_after - t_before) + " seconds") # Check the response and fail if it's bad if check_response: if not api_data["status"] == "success": raise Exception(command_text + " returned bad response, " + api_data["status"]) except urllib2.HTTPError as e: errorTrace("alternativeShellfire.py", command_text + " failed") errorTrace("alternativeShellfire.py", "API call was " + rest_url) if not api_data == "": errorTrace("alternativeShellfire.py", "Data returned was \n" + json.dumps(api_data, indent=4)) errorTrace("alternativeShellfire.py", "Response was " + str(e.code) + " " + e.reason) errorTrace("alternativeShellfire.py", e.read()) rc = False except Exception as e: errorTrace("alternativeShellfire.py", command_text + " failed") errorTrace("alternativeShellfire.py", "API call was " + rest_url) if not api_data == "": errorTrace("alternativeShellfire.py", "Data returned was \n" + json.dumps(api_data, indent=4)) errorTrace("alternativeShellfire.py", "Response was " + str(type(e)) + " " + str(e)) rc = False return rc, api_data
def getNordVPNUserPass(vpn_provider): # Download the opvn file try: download_url = "https://api.nordvpn.com/v1/users/services/credentials" if ifHTTPTrace(): infoTrace("alternativeNord.py", "Getting user credentials " + download_url) else: debugTrace("Getting user credentials") token = getTokenNordVPN() req = urllib2.Request(download_url) req.add_header("Authorization", "token:" + token) t_before = now() response = urllib2.urlopen(req) user_data = json.load(response) t_after = now() response.close() if ifJSONTrace(): infoTrace("alternativeNord.py", "JSON received is \n" + json.dumps(user_data, indent=4)) if t_after - t_before > TIME_WARN: infoTrace( "alternativeNord.py", "Getting user credentials took " + str(t_after - t_before) + " seconds") return user_data["username"], user_data["password"] except urllib2.HTTPError as e: errorTrace("alternativeNord.py", "Couldn't get user credentials") errorTrace("alternativeNord.py", "API call was " + download_url) errorTrace("alternativeNord.py", "Response was " + str(e.code) + " " + e.reason) errorTrace("alternativeNord.py", e.read()) return "", "" except Exception as e: errorTrace("alternativeNord.py", "Couldn't get user credentials") errorTrace("alternativeNord.py", "API call was " + download_url) errorTrace("alternativeNord.py", "Response was " + str(type(e)) + " " + str(e)) return "", ""
def authenticateNordVPN(vpn_provider, userid, password): # Authenticate with the API and store the tokens returned # If the same credentials have been used before, don't bother authenticating _,_,_, creds = getTokens() if creds == vpn_provider + userid + password: debugTrace("Previous authentication was good") return True response = "" try: download_url = "https://api.nordvpn.com/v1/users/tokens" download_data = urllib.urlencode({'username': userid, 'password': password}) if ifHTTPTrace(): infoTrace("alternativeNord.py", "Authenticating with VPN using " + download_url + ", " + download_data) else: debugTrace("Authenticating with VPN for user " + userid) req = Request(download_url, download_data) t_before = now() response = urlopen(req, timeout=10) user_data = json.load(response) t_after = now() response.close() if ifJSONTrace(): infoTrace("alternativeNord.py", "JSON received is \n" + json.dumps(user_data, indent=4)) if t_after - t_before > TIME_WARN: infoTrace("alternativeNord.py", "Authenticating with VPN for " + userid + " took " + str(t_after - t_before) + " seconds") setTokens(user_data["token"], user_data["renew_token"], None) setTokens(user_data["token"], user_data["renew_token"], vpn_provider + userid + password) return True except HTTPError as e: errorTrace("alternativeNord.py", "Couldn't authenticate with " + vpn_provider) errorTrace("alternativeNord.py", "API call was " + download_url + ", " + download_data[:download_data.index("&password")+10] + "********") errorTrace("alternativeNord.py", "Response was " + str(e.code) + " " + e.reason) errorTrace("alternativeNord.py", e.read()) except Exception as e: errorTrace("alternativeNord.py", "Couldn't authenticate with " + vpn_provider) errorTrace("alternativeNord.py", "API call was " + download_url + ", " + download_data[:download_data.index("&password")+10] + "********") errorTrace("alternativeNord.py", "Response was " + str(type(e)) + " " + str(e)) resetTokens() 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 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 sendAPI(command, command_text, api_data, check_response): # Common routine to send an API command try: response = "" rc = True rest_url = REQUEST_URL + command auth_token, _, _, _ = getTokens() # Login again if the token is blank and the command is not login anyway if auth_token == "" and not "=login" in command: debugTrace("Logging in again because auth token not valid") addon = xbmcaddon.Addon(getID()) rc = authenticateShellfire(addon.getSetting("vpn_provider"), addon.getSetting("vpn_username"), addon.getSetting("vpn_password")) auth_token, _, _, _ = getTokens() if auth_token == "" or not rc: raise Exception(command_text + " was not authorized") if ifHTTPTrace(): infoTrace("alternativeShellfire.py", command_text + " " + rest_url) else: debugTrace(command_text) req = urllib2.Request(rest_url, api_data, REQUEST_HEADERS) if not auth_token == "": req.add_header("x-authorization-token", auth_token) t_before = now() response = urllib2.urlopen(req) api_data = json.load(response) t_after = now() response.close() # Trace if the command took a long time if ifJSONTrace(): infoTrace("alternativeShellfire.py", "JSON received is \n" + json.dumps(api_data, indent=4)) if t_after - t_before > TIME_WARN: infoTrace( "alternativeShellfire.py", command_text + " took " + str(t_after - t_before) + " seconds") # Check the response and fail if it's bad if check_response: if not api_data["status"] == "success": raise Exception(command_text + " returned bad response, " + api_data["status"]) except urllib2.HTTPError as e: errorTrace("alternativeShellfire.py", command_text + " failed") errorTrace("alternativeShellfire.py", "API call was " + rest_url) if not api_data == "": errorTrace("alternativeShellfire.py", "Data returned was \n" + json.dumps(api_data, indent=4)) errorTrace("alternativeShellfire.py", "Response was " + str(e.code) + " " + e.reason) errorTrace("alternativeShellfire.py", e.read()) rc = False except Exception as e: errorTrace("alternativeShellfire.py", command_text + " failed") errorTrace("alternativeShellfire.py", "API call was " + rest_url) if not api_data == "": errorTrace("alternativeShellfire.py", "Data returned was \n" + json.dumps(api_data, indent=4)) errorTrace("alternativeShellfire.py", "Response was " + str(type(e)) + " " + str(e)) rc = False return rc, api_data
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 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