SHARE
TWEET

YouTubeLogin.py

kosteksyk Sep 19th, 2013 447 Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. '''
  2.    YouTube plugin for XBMC
  3.    Copyright (C) 2010-2012 Tobias Ussing And Henrik Mosgaard Jensen
  4.  
  5.    This program is free software: you can redistribute it and/or modify
  6.    it under the terms of the GNU General Public License as published by
  7.    the Free Software Foundation, either version 3 of the License, or
  8.    (at your option) any later version.
  9.  
  10.    This program is distributed in the hope that it will be useful,
  11.    but WITHOUT ANY WARRANTY; without even the implied warranty of
  12.    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13.    GNU General Public License for more details.
  14.  
  15.    You should have received a copy of the GNU General Public License
  16.    along with this program.  If not, see <http://www.gnu.org/licenses/>.
  17. '''
  18.  
  19. import re
  20. import sys
  21. import time
  22. try: import simplejson as json
  23. except ImportError: import json
  24.  
  25. # ERRORCODES:
  26. # 0 = Ignore
  27. # 200 = OK
  28. # 303 = See other (returned an error message)
  29. # 500 = uncaught error
  30.  
  31.  
  32. class YouTubeLogin():
  33.     APIKEY = "AI39si6hWF7uOkKh4B9OEAX-gK337xbwR9Vax-cdeF9CF9iNAcQftT8NVhEXaORRLHAmHxj6GjM-Prw04odK4FxACFfKkiH9lg"
  34.  
  35.     urls = {}
  36.     urls[u"oauth_api_login"] = u"https://accounts.google.com/o/oauth2/auth?client_id=208795275779.apps.googleusercontent.com&redirect_uri=urn:ietf:wg:oauth:2.0:oob&scope=http%3A%2F%2Fgdata.youtube.com&response_type=code"
  37.  
  38.     def __init__(self):
  39.         self.xbmc = sys.modules["__main__"].xbmc
  40.  
  41.         self.pluginsettings = sys.modules["__main__"].pluginsettings
  42.         self.settings = sys.modules["__main__"].settings
  43.         self.language = sys.modules["__main__"].language
  44.         self.plugin = sys.modules["__main__"].plugin
  45.         self.dbg = sys.modules["__main__"].dbg
  46.  
  47.         self.utils = sys.modules["__main__"].utils
  48.         self.core = sys.modules["__main__"].core
  49.         self.common = sys.modules["__main__"].common
  50.  
  51.     def login(self, params={}):
  52.         get = params.get
  53.         self.common.log("")
  54.  
  55.         old_user_name = self.pluginsettings.userName()
  56.         old_user_password = self.pluginsettings.userPassword()
  57.         self.settings.openSettings()
  58.  
  59.         user_name = self.pluginsettings.userName()
  60.         user_password = self.pluginsettings.userPassword()
  61.  
  62.         self.dbg = self.pluginsettings.debugModeIsEnabled()
  63.         result = ""
  64.         status = 500
  65.  
  66.         if not user_name:
  67.             return (result, 200)
  68.  
  69.         refreshed = False
  70.         if get("new", "false") == "false" and self.pluginsettings.authenticationRefreshRoken and old_user_name == user_name and old_user_password == user_password:
  71.             self.common.log("refreshing token: " + str(refreshed))
  72.             refreshed = self.core._oRefreshToken()
  73.  
  74.         if not refreshed:
  75.             result, status = self.authorize()
  76.  
  77.         self.xbmc.executebuiltin("Container.Refresh")
  78.         return (result, status)
  79.  
  80.     def authorize(self):
  81.         self.common.log("token not refresh, or new uname or password")
  82.         self.settings.setSetting("oauth2_access_token", "")
  83.         self.settings.setSetting("oauth2_refresh_token", "")
  84.         self.settings.setSetting("oauth2_expires_at", "")
  85.         (result, status) = self._httpLogin({"new": "true"})
  86.         if status == 200:
  87.             (result, status) = self._apiLogin()
  88.         if status == 200:
  89.             self.utils.showErrorMessage(self.language(30031), result, 303)
  90.         else:
  91.             self.utils.showErrorMessage(self.language(30609), result, status)
  92.         return result, status
  93.  
  94.     def _apiLogin(self):
  95.         self.common.log("")
  96.  
  97.         url = self.urls[u"oauth_api_login"]
  98.  
  99.         logged_in = False
  100.         fetch_options = {"link": url, "no-language-cookie": "true"}
  101.         step = 0
  102.         self.common.log("Part A")
  103.         while not logged_in and fetch_options and step < 6:
  104.             self.common.log("Step : " + str(step))
  105.             step += 1
  106.  
  107.             ret = self.core._fetchPage(fetch_options)
  108.             fetch_options = False
  109.  
  110.             newurl = self.common.parseDOM(ret["content"], "form", attrs={"method": "POST"}, ret="action")
  111.             state_wrapper = self.common.parseDOM(ret["content"], "input", attrs={"id": "state_wrapper"}, ret="value")
  112.  
  113.             if len(newurl) > 0 and len(state_wrapper) > 0:
  114.                 url_data = {"state_wrapper": state_wrapper[0],
  115.                             "submit_access": "true"}
  116.  
  117.                 fetch_options = {"link": newurl[0].replace("&amp;", "&"), "url_data": url_data, "no-language-cookie": "true"}
  118.                 self.common.log("Part B")
  119.                 continue
  120.  
  121.             code = self.common.parseDOM(ret["content"], "input", attrs={"id": "code"}, ret="value")
  122.             if len(code) > 0:
  123.                 url = "https://accounts.google.com/o/oauth2/token"
  124.                 url_data = {"client_id": "208795275779.apps.googleusercontent.com",
  125.                             "client_secret": "sZn1pllhAfyonULAWfoGKCfp",
  126.                             "code": code[0],
  127.                             "redirect_uri": "urn:ietf:wg:oauth:2.0:oob",
  128.                             "grant_type": "authorization_code"}
  129.                 fetch_options = {"link": url, "url_data": url_data}
  130.                 self.common.log("Part C")
  131.                 continue
  132.  
  133.             # use token
  134.             if ret["content"].find("access_token") > -1:
  135.                 self.common.log("Part D")
  136.                 oauth = json.loads(ret["content"])
  137.  
  138.                 if len(oauth) > 0:
  139.                     self.common.log("Part D " + repr(oauth["expires_in"]))
  140.                     self.settings.setSetting("oauth2_expires_at", str(int(oauth["expires_in"]) + time.time()))
  141.                     self.settings.setSetting("oauth2_access_token", oauth["access_token"])
  142.                     self.settings.setSetting("oauth2_refresh_token", oauth["refresh_token"])
  143.  
  144.                     logged_in = True
  145.                     self.common.log("Done:" + self.settings.getSetting("username"))
  146.  
  147.         if logged_in:
  148.             return (self.language(30030), 200)
  149.         else:
  150.             self.common.log("Failed")
  151.             return (self.language(30609), 303)
  152.  
  153.     def _httpLogin(self, params={}):
  154.         get = params.get
  155.         self.common.log("")
  156.         status = 500
  157.  
  158.         if get("new", "false") == "true" or get("page", "false") != "false":
  159.             self.settings.setSetting("login_info", "")
  160.             self.settings.setSetting("SID", "")
  161.             self.settings.setSetting("login_cookies", "")
  162.         elif self.settings.getSetting("login_info") != "":
  163.             self.common.log("returning existing login info: " + self.settings.getSetting("login_info"))
  164.             return (self.settings.getSetting("login_info"), 200)
  165.  
  166.         fetch_options = {"link": get("link", "http://www.youtube.com/")}
  167.  
  168.         step = 0
  169.         galx = ""
  170.         ret = {}
  171.  
  172.         while fetch_options and step < 18:  # 6 steps for 2-factor login
  173.             self.common.log("Step : " + str(step))
  174.             step += 1
  175.  
  176.             if step == 17:
  177.                 return (self.core._findErrors(ret), 303)
  178.  
  179.             ret = self.core._fetchPage(fetch_options)
  180.  
  181.             if ret["content"].find(" captcha") > -1:
  182.                 self.common.log("Captcha needs to be filled")
  183.                 break
  184.             fetch_options = False
  185.  
  186.             # Check if we are logged in.
  187.             nick = self.common.parseDOM(ret["content"], "p", attrs={"class": "masthead-expanded-acct-sw-id2"})
  188.  
  189.             # Check if there are any errors to report
  190.             errors = self.core._findErrors(ret, silent=True)
  191.             if errors:
  192.                 if errors.find("cookie-clear-message-1") == -1 and (errors.find("The code you entered didn") == -1 or (errors.find("The code you entered didn") > -1 and step > 12)):
  193.                     self.common.log("Returning error: " + repr(errors))
  194.                     return (errors, 303)
  195.  
  196.             if len(nick) > 0 and nick[0] != "Sign In":
  197.                 self.common.log("Logged in. Parsing data: " + repr(nick))
  198.                 status = self._getLoginInfo(nick)
  199.                 return(ret, status)
  200.  
  201.             # Click login link on youtube.com
  202.             newurl = self.common.parseDOM(ret["content"], "button", attrs={"href": ".*?ServiceLogin.*?"}, ret="href")
  203.             if len(newurl) > 0:
  204.                 # Start login procedure
  205.                 if newurl[0] != "#":
  206.                     fetch_options = {"link": newurl[0].replace("&amp;", "&"), "referer": ret["location"]}
  207.                     self.common.log("Part A : " + repr(fetch_options))
  208.  
  209.             # Fill out login information and send.
  210.             newurl = self.common.parseDOM(ret["content"].replace("\n", " "), "form", attrs={"id": "gaia_loginform"}, ret="action")
  211.             if len(newurl) > 0:
  212.                 (galx, url_data) = self._fillLoginInfo(ret)
  213.                 if len(galx) > 0 and len(url_data) > 0:
  214.                     fetch_options = {"link": newurl[0], "no-language-cookie": "true", "url_data": url_data, "hidden": "true", "referer": ret["location"]}
  215.                     self.common.log("Part B")
  216.                     self.common.log("fetch options: " + repr(fetch_options), 10)  # WARNING, SHOWS LOGIN INFO/PASSWORD
  217.                     continue
  218.  
  219.             newurl = self.common.parseDOM(ret["content"], "meta", attrs={"http-equiv": "refresh"}, ret="content")
  220.             if len(newurl) > 0:
  221.                 newurl = newurl[0].replace("&amp;", "&")
  222.                 newurl = newurl[newurl.find("&#39;") + 5:newurl.rfind("&#39;")]
  223.                 fetch_options = {"link": newurl, "no-language-cookie": "true", "referer": ret["location"]}
  224.                 self.common.log("Part C: "  + repr(fetch_options))
  225.                 continue
  226.  
  227.             ## 2-factor login start
  228.             if ret["content"].find("smsUserPin") > -1:
  229.                 url_data = self._fillUserPin(ret["content"])
  230.                 if len(url_data) == 0:
  231.                     return (False, 500)
  232.  
  233.                 new_part = self.common.parseDOM(ret["content"], "form", attrs={"name": "verifyForm"}, ret="action")
  234.                 fetch_options = {"link": new_part[0], "url_data": url_data, "no-language-cookie": "true", "referer": ret["location"]}
  235.  
  236.                 self.common.log("Part D: " + repr(fetch_options))
  237.                 continue
  238.  
  239.             smsToken = self.common.parseDOM(ret["content"].replace("\n", ""), "input", attrs={"name": "smsToken"}, ret="value")
  240.  
  241.             if len(smsToken) > 0 and galx != "":
  242.                 url_data = {"smsToken": smsToken[0],
  243.                             "PersistentCookie": "yes",
  244.                             "service": "youtube",
  245.                             "GALX": galx}
  246.  
  247.                 target_url = self.common.parseDOM(ret["content"], "form", attrs={"name": "hiddenpost"}, ret="action")
  248.                 fetch_options = {"link": target_url[0], "url_data": url_data, "no-language-cookie": "true", "referer": ret["location"]}
  249.                 self.common.log("Part E: " + repr(fetch_options))
  250.                 continue
  251.  
  252.             ## 2-factor login finish
  253.             if not fetch_options:
  254.                 # Check for errors.
  255.                 return (self.core._findErrors(ret), 303)
  256.  
  257.         return (ret, status)
  258.  
  259.     def _fillLoginInfo(self, ret):
  260.         content = ret["content"]
  261.         rmShown = self.common.parseDOM(content, "input", attrs={"name": "rmShown"}, ret="value")
  262.         cont = self.common.parseDOM(content, "input", attrs={"name": "continue"}, ret="value")
  263.         uilel = self.common.parseDOM(content, "input", attrs={"name": "uilel"}, ret="value") # Deprecated?
  264.         if len(uilel) == 0: # Deprecated?
  265.             uilel = self.common.parseDOM(content, "input", attrs= {"id":"uilel"}, ret="value")
  266.         if len(uilel) == 0 and ret["new_url"].find("uilel=") > -1:
  267.             uilel = ret["new_url"][ret["new_url"].find("uilel=")+6]
  268.             if uilel.find("&") > -1:
  269.                 uilel = uilel[:uilel.find("&")]
  270.             uilel = [uilel]
  271.         dsh = self.common.parseDOM(content, "input", attrs={"name": "dsh"}, ret="value")
  272.         if len(dsh) == 0:
  273.             dsh = self.common.parseDOM(content, "input", attrs={"id": "dsh"}, ret="value")
  274.  
  275.         galx = self.common.parseDOM(content, "input", attrs={"name": "GALX"}, ret="value")
  276.         uname = self.pluginsettings.userName()
  277.         pword = self.pluginsettings.userPassword()
  278.  
  279.         if pword == "":
  280.             pword = self.common.getUserInput(self.language(30628), hidden=True)
  281.  
  282.         if len(galx) == 0 or len(cont) == 0 or len(uilel) == 0 or len(dsh) == 0 or len(rmShown) == 0 or uname == "" or pword == "":
  283.             self.common.log("_fillLoginInfo missing values for login form " + repr(galx) + repr(cont) + repr(uilel) + repr(dsh) + repr(rmShown) + repr(uname) + str(len(pword)))
  284.             return ("", {})
  285.         else:
  286.             galx = galx[0]
  287.             url_data = {"pstMsg": "0",
  288.                         "ltmpl": "sso",
  289.                         "dnConn": "",
  290.                         "continue": cont[0],
  291.                         "service": "youtube",
  292.                         "uilel": uilel[0],
  293.                         "dsh": dsh[0],
  294.                         "hl": "en_US",
  295.                         "timeStmp": "",
  296.                         "secTok": "",
  297.                         "GALX": galx,
  298.                         "Email": uname,
  299.                         "Passwd": pword,
  300.                         "PersistentCookie": "yes",
  301.                         "rmShown": rmShown[0],
  302.                         "signin": "Sign in",
  303.                         "asts": ""
  304.                         }
  305.         return (galx, url_data)
  306.  
  307.     def _fillUserPin(self, content):
  308.         self.common.log(repr(content), 5)
  309.         smsToken = self.common.parseDOM(content, "input", attrs={"name": "smsToken"}, ret="value")
  310.         self.smsToken = smsToken
  311.         userpin = self.common.getUserInputNumbers(self.language(30627))
  312.  
  313.         if len(userpin) > 0:
  314.             url_data = {"smsToken": smsToken[0],
  315.                         "PersistentCookie": "yes",
  316.                         "smsUserPin": userpin,
  317.                         "smsVerifyPin": "Verify",
  318.                         "timeStmp": "",
  319.                         "secTok": ""}
  320.             self.common.log("Done: " + repr(url_data))
  321.             return url_data
  322.         else:
  323.             self.common.log("Replace this with a message telling users that they didn't enter a pin")
  324.             return {}
  325.  
  326.     def _getLoginInfo(self, nick):
  327.         self.common.log(nick)
  328.         status = 303
  329.  
  330.         # Save cookiefile in settings
  331.         cookies = self.common.getCookieInfoAsHTML()
  332.         login_info = self.common.parseDOM(cookies, "cookie", attrs={"name": "LOGIN_INFO"}, ret="value")
  333.         SID = self.common.parseDOM(cookies, "cookie", attrs={"name": "SID", "domain": ".youtube.com"}, ret="value")
  334.         scookies = {}
  335.         self.common.log("COOKIES:" + repr(cookies))
  336.         tnames = re.compile(" name='(.*?)' ").findall(cookies)
  337.         for key in tnames:
  338.             tval = self.common.parseDOM(cookies, "cookie", attrs={"name": key}, ret="value")
  339.             if len(tval) > 0:
  340.                 scookies[key] = tval[0]
  341.         self.common.log("COOKIES:" + repr(scookies))
  342.  
  343.         if len(login_info) == 1:
  344.             self.common.log("LOGIN_INFO: " + repr(login_info))
  345.             self.settings.setSetting("login_info", login_info[0])
  346.         else:
  347.             self.common.log("Failed to get LOGIN_INFO from youtube: " + repr(login_info))
  348.  
  349.         if len(SID) == 1:
  350.             self.common.log("SID: " + repr(SID))
  351.             self.settings.setSetting("SID", SID[0])
  352.         else:
  353.             self.common.log("Failed to get SID from youtube: " + repr(SID))
  354.  
  355.         if len(SID) == 1 and len(login_info) == 1:
  356.             status = 200
  357.             self.settings.setSetting("login_cookies", repr(scookies))
  358.  
  359.         self.common.log("Done")
  360.         return status
RAW Paste Data
We use cookies for various purposes including analytics. By continuing to use Pastebin, you agree to our use of cookies as described in the Cookies Policy. OK, I Understand
 
Top