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 = Request(download_url) req.add_header("Authorization", "token:" + token) 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", "Getting user credentials took " + str(t_after - t_before) + " seconds") return user_data["username"], user_data["password"] except 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 renewNordVPN(renew): # Renew a user with the API and store the tokens returned response = "" try: download_url = "https://api.nordvpn.com/v1/users/tokens/renew" download_data = "renewToken=" + renew if ifHTTPTrace(): infoTrace("alternativeNord.py", "Renewing authentication using " + download_url + ", " + download_data) else: debugTrace("Renewing authentication") 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", "Renewing authentication took " + str(t_after - t_before) + " seconds") setTokens(user_data["token"], user_data["renew_token"], None) return True except HTTPError as e: errorTrace("alternativeNord.py", "Couldn't renew user token") errorTrace("alternativeNord.py", "API call was " + download_url + ", " + download_data[:download_data.index("renewToken")+11] + "********") errorTrace("alternativeNord.py", "Response was " + str(e.code) + " " + e.reason) errorTrace("alternativeNord.py", e.read()) except Exception as e: errorTrace("alternativeNord.py", "Couldn't renew user token") errorTrace("alternativeNord.py", "API call was " + download_url + ", " + download_data[:download_data.index("renewToken")+11] + "********") errorTrace("alternativeNord.py", "Response was " + str(type(e)) + " " + str(e)) resetTokens() return False
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 = urllib2.Request(download_url, download_data) t_before = now() response = urllib2.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 urllib2.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 getNordVPNOvpnFile(server, protocol, target_file): # Download the opvn file try: download_url = "https://downloads.nordcdn.com/configs/files/ovpn_" + protocol + "/servers/" + server + "." + protocol + ".ovpn" if ifHTTPTrace(): infoTrace( "alternativeNord.py", "Downloading ovpn for " + server + ", protocol " + protocol + " using " + download_url) else: debugTrace("Downloading ovpn for " + server + ", protocol " + protocol) token = getTokenNordVPN() req = urllib2.Request(download_url) req.add_header("Authorization", "token:" + token) t_before = now() response = urllib2.urlopen(req, timeout=10) lines = response.readlines() t_after = now() response.close() if t_after - t_before > TIME_WARN: infoTrace( "alternativeNord.py", "Downloading ovpn for " + server + ", protocol " + protocol + " took " + str(t_after - t_before) + " seconds") except urllib2.HTTPError as e: errorTrace( "alternativeNord.py", "Couldn't download the ovpn for server " + server + ", protocol " + protocol) errorTrace("alternativeNord.py", "API call was " + download_url) errorTrace("alternativeNord.py", "Response was " + str(e.code) + " " + e.reason) errorTrace("alternativeNord.py", e.read()) return False except Exception as e: errorTrace( "alternativeNord.py", "Couldn't download the ovpn for server " + server + ", protocol " + protocol) errorTrace("alternativeNord.py", "API call was " + download_url) errorTrace("alternativeNord.py", "Response was " + str(type(e)) + " " + str(e)) return False try: debugTrace("Writing ovpn file to " + target_file) f = open(target_file, 'w') for line in lines: line = line.strip(' \t\n\r') f.write(line + "\n") f.close() return True except Exception as e: errorTrace("alternativeNord.py", "Couldn't write ovpn to " + target_file) 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 getTokenNordVPN(): # Return a token that can be used on API calls token, renew, expiry, _ = getTokens() # If the expiry time is passed, renew the token if expiry.isdigit() and int(expiry) < now(): if renewNordVPN(renew): token, _, _, _ = getTokens() return token else: # Force an authenticate to happen token = "" # The authentication call is made during connection validation, which will validate everything and fetch # the tokens. If a reboot happens and the tokens disappear, then we need to force an authenticate again if token == "": addon = xbmcaddon.Addon(getID()) if authenticateNordVPN(addon.getSetting("vpn_provider_validated"), addon.getSetting("vpn_username_validated"), addon.getSetting("vpn_password_validated")): token, _, _, _ = getTokens() return token else: errorTrace("alternativeNord.py", "Couldn't authenticate or renew the user ID") resetTokens() raise RuntimeError("Couldn't get a user ID token") debugTrace("Using existing user ID token") return token
def recordAction(action): log = getActionLogName(False) old_log = getActionLogName(True) addon = xbmcaddon.Addon("service.zomboided.tools") if addon.getSetting("enable_action_log") == "true": try: if xbmcvfs.exists(log): st = xbmcvfs.Stat(log) size = st.st_size() if size > 1024000: # Limit log files to 1MB...this allow for ~10000 entries debugTrace("Action log size is " + str(size) + ", starting new action log") if xbmcvfs.exists(old_log): xbmcvfs.delete(old_log) xbmcvfs.rename(log, old_log) except Exception as e: errorTrace("common.py", "Couldn't manage existing action log file") errorTrace("common.py", str(e)) try: log_file = open(log, 'a+') time = datetime.datetime.fromtimestamp(now()) log_file.write(str(time) + " " + action + "\n") log_file.close() except Exception as e: errorTrace("common.py", "Couldn't record action") errorTrace("common.py", str(e))
def getShellfireMessages(vpn_provider, last_time, last_id): # Return any message ID and message available from the provider # Never return a message for a paid account unless a last_time of 0 is being used to force it if getAccountType() > 0 and not last_time == 0: return "", "" # Fetch any available deal rc, api_data = sendAPI("?action=getAvailablePricingDeal", "Retrieving messages", "", False) # Adding 'Success' to the end of this line will return a test message # Check the call worked and that it was successful. If there's no message, a bad response is returned if not rc: return "", "" if not api_data["status"] == "success": return "", "" try: # Extract and format the message id = api_data["data"]["pricingDealId"] message = api_data["data"]["name"] + " - " + api_data["data"]["description"] + " - Only available until " ts = int(api_data["data"]["validUntil"]) message = message + time.strftime("%b %d", time.gmtime(ts)) message = message + " - " + api_data["data"]["url"] # Don't return a message if the same message was displayed < 1 week ago if (not last_time == 0) and (last_time + 604800 > now()) and last_id == id: return "", "" except Exception as e: errorTrace("alternativeShellfire.py", "Couldn't format message returned") errorTrace("alternativeShellfire.py", "JSON received is \n" + json.dumps(api_data, indent=4)) return "", "" return id, message
def setTokens(token, renew, creds): xbmcgui.Window(10000).setProperty("VPN_Manager_Alternative_Token", token) xbmcgui.Window(10000).setProperty("VPN_Manager_Alternative_Renew", renew) # Renew time is a day after token creation if not renew == "": xbmcgui.Window(10000).setProperty("VPN_Manager_Alternative_Expiry", str(now() + 86400)) else: xbmcgui.Window(10000).setProperty("VPN_Manager_Alternative_Expiry", "") if not creds == None: xbmcgui.Window(10000).setProperty( "VPN_Manager_Alternative_Credentials", creds)
def runPlaybackEnded(self, caller): global playback_timer global sleep_timer self.playing_file = False playback_timer = 0 t = now() self.playlist_ended = t self.playlist_count += 1 debugTrace("Playback ended called from " + caller + ", at " + str(self.playlist_ended) + ", count is " + str(self.playlist_count) + " sleep is " + getSleep()) if getSleep() == SLEEP_END: addAlert() sleep_timer = 1
def getShellfireMessages(vpn_provider, last_time, last_id): # Return any message ID and message available from the provider # Never return a message for a paid account unless a last_time of 0 is being used to force it if getAccountType() > 0 and not last_time == 0: return "", "" # Fetch any available deal rc, api_data = sendAPI("?action=getAvailablePricingDealSuccess", "Retrieving messages", "", False) # FIXME Adding 'Success' to the end of this line will return a test message # Check the call worked and that it was successful. If there's no message, a bad response is returned if not rc: return "", "" if not api_data["status"] == "success": return "", "" try: # Extract and format the message id = api_data["data"]["pricingDealId"] message = api_data["data"]["name"] + " - " + api_data["data"][ "description"] + " - Only available until " ts = int(api_data["data"]["validUntil"]) message = message + time.strftime("%b %d", time.gmtime(ts)) message = message + " - " + api_data["data"]["url"] # Don't return a message if the same message was displayed < 1 week ago if (not last_time == 0) and (last_time + 604800 > now()) and last_id == id: return "", "" except Exception as e: errorTrace("alternativeShellfire.py", "Couldn't format message returned") errorTrace("alternativeShellfire.py", "JSON received is \n" + json.dumps(api_data, indent=4)) return "", "" return id, message
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 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
try: api = VPNAPI() debugTrace("Found VPN Manager") except Exception as e: errorTrace("service.py", "Couldn't connect to the VPN Mgr API") errorTrace("service.py", str(e)) api = None # Initialise some variables we'll be using repeatedly addon = xbmcaddon.Addon() allowUpdates(False) addon.setSetting("boot_time", time.strftime('%Y-%m-%d %H:%M:%S')) allowUpdates(True) last_boot = now() updateSettings("main initialisation", True) # Initialise a bunch of variables delay = 10 delay_loop = 6 file_timer = 0 last_file_check = 0 action_number_f = 0 addon_timer = 0 last_addon_check = 0 action_number_a = 0 last_sleep = SLEEP_OFF sleep_timer = 0
def onPlayBackStarted(self, *arg): global playback_timer addon = xbmcaddon.Addon() # Deal with endPlayback not being called previously if self.playing_file: self.runPlaybackEnded() self.playing_file = True t = now() try: infoTrace("service.py", "Playing " + self.getPlayingFile()) recordAction("Playing " + self.getPlayingFile()) except: pass # Determine the end time if there's a play back limit d_timer = 0 t_timer = 0 playback_timer = 0 if playback_duration_check: d_timer = t + (playback_duration_minutes * 60) if playback_time_check: t_timer = parseTimer("Play back timer", "Daily", playback_time, "", "", "", 0) if not d_timer == 0 and (d_timer < t_timer or t_timer == 0): playback_timer = d_timer if not t_timer == 0 and (t_timer < d_timer or d_timer == 0): playback_timer = t_timer if playback_timer: debugTrace("Time is " + str(t) + " playing until " + str(playback_timer) + ". Duration was " + str(playback_duration_minutes) + ", time was " + playback_time) # If playback ended some time ago, this isn't a playlist if self.playlist_playing and t - self.playlist_ended > playlist_detection_delay: debugTrace("Playlist is over") debugTrace("Ended because time between videos was " + str(t-self.playlist_ended) + " seconds, max is " + str(playlist_detection_delay)) self.resetPlaybackCounts() if not self.playlist_playing: # If a playlist isn't active, this is the first video in a playlist (or otherwise) updateSettings("onPlayBackStarted", True) self.playlist_playing = True self.playlist_max = t + (playlist_max_minutes * 60) self.playlist_count = 1 debugTrace("Detected playback starting") debugTrace("Max playback time is " + str(playlist_max_minutes)) debugTrace("Max playback videos is " + str(playlist_max_count)) debugTrace("Time is " + str(t) + " max time is " + str(self.playlist_max)) else: # This is not the first video in a playlist if playlist_count_check: debugTrace("Count is " + str(self.playlist_count) + ", max is " + str(playlist_max_count) + ", min is " + str(playlist_min_count)) if playlist_time_check: debugTrace("Time is " + str(t) + ", timeout is " + str(self.playlist_max)) if playlist_count_check and self.playlist_count >= playlist_max_count: xbmc.Player().stop() infoTrace("service.py", "Stopping playlist after playing " + str(playlist_max_count) + " videos.") self.resetPlaybackCounts() elif playlist_time_check and t > self.playlist_max and self.playlist_count >= playlist_min_count: xbmc.Player().stop() infoTrace("service.py", "Stopping playlist after reaching maximum time period") self.resetPlaybackCounts() if addon.getSetting("auto_sleep") == "true" and t > auto_sleep_start and t < auto_sleep_end: if getSleep() == SLEEP_OFF: debugTrace("Setting auto sleep") setSleepReq("End") setSleepReqTime(t-SLEEP_DELAY_TIME) xbmcgui.Dialog().notification("Sleeping at end of video." , "", "", 2000, False) addAlert()
def parseTimer(type, freq, rtime, day, date, period, begin): debugTrace("Parsing " + type + ". Frequency is " + freq + ", time is + " + rtime + ", day is " + day + ", date is " + date + ", period is " + period + ", begin is " + str(begin)) if freq == "" or freq == "Off": return 0 else: # Assume timer is today at the defined reboot time timer = time.strftime("%d %m %Y") + " " + rtime # Parse the timer starting from a given time, or now if begin > 0: t = begin else: t = now() # Make some datetime objects representing now, last boot time and the timer current_dt = datetime.datetime.fromtimestamp(t) last_dt = datetime.datetime.fromtimestamp(last_boot) #xbmc.sleep(1000) #try: # timer_dt = datetime.datetime.strptime(timer, "%d %m %Y %H:%M") #except: timer_dt = datetime.datetime(*(time.strptime(timer, "%d %m %Y %H:%M")[0:6])) # Adjust timer based on the frequency if freq == "Daily": # If the timer is in the past, add a day if timer_dt < current_dt and not begin == TODAY: d = datetime.timedelta(days = 1) timer_dt = timer_dt + d elif freq == "Weekly": # Calculate the next boot day current_day = current_dt.weekday() timer_day = days.index(day) if current_day > timer_day: adjust_day = 7 - (current_day - timer_day) elif current_day < timer_day: adjust_day = timer_day - current_day else: # Same day, if it's in the past, add a week if timer_dt < current_dt: adjust_day = 7 else: adjust_day = 0 # Adjust the timer the required number of days if adjust_day > 0: d = datetime.timedelta(days = adjust_day) timer_dt = timer_dt + d elif freq == "Monthly": new_day = int(date) if (current_dt.day > new_day) or (current_dt.day == new_day and timer_dt < current_dt): if current_dt.month == 12: timer_dt = timer_dt.replace(month = 1) timer_dt = timer_dt.replace(year = timer_dt.year + 1) else: timer_dt = timer_dt.replace(month = timer_dt.month + 1) _, max_day = calendar.monthrange(timer_dt.year, timer_dt.month) if new_day > max_day: new_day = max_day timer_dt = timer_dt.replace(day = new_day) elif freq == "Periodically": timer_dt == datetime.datetime.combine(last_dt.date(), timer_dt.timetz()) d = datetime.timedelta(days = int(period)) timer_dt = timer_dt + d else: errorTrace("service.py", "Couldn't parse timer, no timer set") return 0 # Calculate the difference between the dates and return the timer value in epoch seconds debugTrace("Starting time is " + str(current_dt) + ", last reboot is " + str(last_dt) + ", timer is " + str(timer_dt)) diff_seconds = int((timer_dt - current_dt).total_seconds()) return (t + diff_seconds)
def updateSettings(caller, wait): global playback_duration_check global playback_duration_minutes global playback_time_check global playback_time global playlist_detection_delay global playlist_time_check global playlist_max_minutes global playlist_count_check global playlist_max_count global playlist_min_count global action_timer global action_timer_number global addon_timer global addon_timer_start global addon_timer_end global addon_check_freq global file_timer global file_timer_start global file_timer_end global file_check_freq global refresh_timer global refresh_check_freq global auto_sleep_start global auto_sleep_end if not updatesAllowed() and not wait: return # Wait for 60 seconds, then carry on anyway for i in range(1, 60): if updatesAllowed(): break xbmc.sleep(1000) if i == 59: errorTrace("service.py", "updateSettings was called from " + caller + " with wait, but could not get a lock to update after 60 seconds") debugTrace("updateSettings called from " + caller) refresh_timer = 0 allowUpdates(False) addon = xbmcaddon.Addon() # Refresh the play limit settings if addon.getSetting("stop_playback_duration") == "true": playback_duration_check = True else: playback_duration_check = False playback_duration_minutes = int(addon.getSetting("stop_playback_mins")) if addon.getSetting("stop_playback_after") == "true": playback_time_check = True else: playback_time_check = False playback_time = addon.getSetting("stop_playback_time") # Refresh the playlist settings if addon.getSetting("stop_playlist_time") == "true": playlist_time_check = True else: playlist_time_check = False playlist_max_minutes = int(addon.getSetting("stop_playlist_mins")) if addon.getSetting("stop_playlist_videos") == "true": playlist_count_check = True else: playlist_count_check = False playlist_max_count = int(addon.getSetting("stop_playlist_count")) playlist_min_count = int(addon.getSetting("detect_playlist_minimum")) playlist_detection_delay = int(addon.getSetting("detect_playlist_gap")) # The warning timers should be 10 second increments so fix if it's not for i in range(1, (ACTION_COUNT + 1)): fixWarnTime("action_warn_" + str(i)) fixWarnTime("sleep_warn") addon = xbmcaddon.Addon() # Refresh the action settings action_timer = 0 action_timer_number = 0 for i in range(0, ACTION_COUNT): # Check the timer settings j = str(i+1) # Start counting periodical timers from the time they were set. If it's not # a periodical timer, then reset it so it starts from now next time it's set if addon.getSetting("action_timer_freq_" + j) == "Periodically": if addon.getSetting("action_period_last_" + j) == "": addon.setSetting("action_period_last_" + j, str(now())) begin = int(addon.getSetting("action_period_last_" + j)) else: addon.setSetting("action_period_last_" + j, "") begin = 0 next_action_timer = parseTimer("Action Timer " + j, addon.getSetting("action_timer_freq_" + j), addon.getSetting("action_time_" + j), addon.getSetting("action_day_" + j), addon.getSetting("action_date_" + j), addon.getSetting("action_period_" + j), begin) # Determine if this time is the nearest one and use it if it is if (action_timer == 0 and not next_action_timer == 0) or (next_action_timer > 0 and next_action_timer < action_timer): action_timer_number = i + 1 action_timer = next_action_timer if addon.getSetting("action_addon_enabled_" + j) == "true": selected = addon.getSetting("action_addon_" + j) last = addon.getSetting("addon_name_" + j) if not selected == last: addon_timer = addon_check_freq + 1 t = now() # Frequency add-ons are checked addon_check_freq = int(addon.getSetting("addon_check"))*60 addon_timer_start, addon_timer_end = parseTimePeriod(t, "Addon Check", addon.getSetting("addon_check_start"), addon.getSetting("addon_check_end")) # Frequency files are checked file_check_freq = int(addon.getSetting("file_check"))*60 file_timer_start, file_timer_end = parseTimePeriod(t, "File Check", addon.getSetting("file_check_start"), addon.getSetting("file_check_end")) # Auto sleep timers auto_sleep_start, auto_sleep_end = parseTimePeriod(t, "Auto Sleep", addon.getSetting("auto_sleep_start"), addon.getSetting("auto_sleep_end")) # Settings get refreshed infrequently, just in case... refresh_check_freq = int(addon.getSetting("refresh_check"))*60 if not action_timer_number == 0: debugTrace("Action timer " + str(action_timer_number) + " is the first timer with " + str(action_timer)) else: debugTrace("No action timers are set") # Create the buttons file and make it autostart getButtonCommands() addon = xbmcaddon.Addon() if makeButtonsFile(): debugTrace("Created a new button file") if fixAutostart(): debugTrace("Updated autostart to run zbutton.py") allowUpdates(True)
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 requestSleep(): # Don't know where this was called from so using plugin name to get addon handle addon = xbmcaddon.Addon("service.zomboided.tools") addon_name = addon.getAddonInfo("name") # Don't sleep if we can't get a lock if getSleepLock(): t = now() remain = getSleepRemaining() if t - getSleepReqTime() < SLEEP_DELAY_TIME: # The last time sleep was requested was very recently so move to next value current = getSleepReq() if current == "": current = getSleep() if current == SLEEP_OFF: current = "End" elif current == SLEEP_END: current = addon.getSetting("sleep_inc") elif current.isdigit(): current_int = int(current) if getSleepReq() == "" and remain.isdigit(): # This deals with the case where a timer is running already current_int = int(remain) sleep_max = int(addon.getSetting("sleep_max")) sleep_inc = int(addon.getSetting("sleep_inc")) if current_int >= sleep_max: current = SLEEP_OFF else: if current_int + sleep_inc > sleep_max: current = addon.getSetting("sleep_max") else: current = str( ((current_int / sleep_inc) + 1) * sleep_inc) else: current == SLEEP_OFF setSleepReq(current) debugTrace("Repeat sleep request, " + current + ", remain is " + "") else: # Otherwise get the value of the current sleep state and display it if remain == "": current = getSleep() else: current = remain debugTrace("New sleep request, " + current + ", remain is " + "") if current.isdigit(): if current == "0": xbmcgui.Dialog().notification("Sleeping in less than a minute", "", "", 2000, False) elif current == "1": xbmcgui.Dialog().notification("Sleeping in 1 minute", "", "", 2000, False) else: xbmcgui.Dialog().notification( "Sleeping in " + current + " minutes", "", "", 2000, False) else: if current == SLEEP_END: xbmcgui.Dialog().notification("Sleeping at end of video", "", "", 2000, False) else: xbmcgui.Dialog().notification("Sleep is off", "", "", 2000, False) addAlert() setSleepReqTime(t) xbmc.sleep(1000) freeSleepLock()