Navigation Menu

Skip to content
This repository has been archived by the owner on Jan 19, 2022. It is now read-only.

Commit

Permalink
Use getAddonData('path'), auto-fetch/store keys.
Browse files Browse the repository at this point in the history
  • Loading branch information
spbogie committed Jul 12, 2011
1 parent 771b0f1 commit 2aaaf5e
Show file tree
Hide file tree
Showing 7 changed files with 132 additions and 61 deletions.
1 change: 1 addition & 0 deletions .gitignore
Expand Up @@ -3,3 +3,4 @@ crypt_key_*.h
*.pyo
*~
.*project
*.swp
34 changes: 13 additions & 21 deletions default.py
Expand Up @@ -5,20 +5,17 @@
import xbmc, os
import xbmcaddon

try:
from libpandora.pandora import Pandora
except AttributeError, e:
xbmcgui.Dialog().ok( "PANDORA", \
"ERROR: Something is wrong with your encryption keys." )
raise e
from libpandora.pandora import Pandora

from pandagui import PandaGUI
from pandaplayer import PandaPlayer

__title__ = "Pandora"
__settings__ = xbmcaddon.Addon(id='script.xbmc.pandora')
__script_id__ = "script.xbmc.pandora"
__settings__ = xbmcaddon.Addon(id=__script_id__)

scriptPath = os.getcwd().replace(';','')
scriptPath = __settings__.getAddonInfo('path')
dataDir = os.path.join( "special://profile/addon_data/%s/" %__script_id__ )

class PandaException( Exception ):
pass
Expand All @@ -35,8 +32,9 @@ def __init__( self ):
self.die = False
self.settings = __settings__

fmt = self.settings.getSetting( "format" )
self.pandora = Pandora( fmt )
fmt = int(self.settings.getSetting( "format" ))
fmt = ( "aacplus", "mp3", "mp3-hifi" )[fmt]
self.pandora = Pandora( dataDir, fmt )

self.pandora.sync()

Expand All @@ -52,9 +50,7 @@ def __init__( self ):
return

self.player = PandaPlayer( panda = self )
scriptSkinPath = os.path.join(scriptPath,"resources")
self.gui = PandaGUI( "script-pandora.xml", scriptPath, \
"Default", "NTSC", panda = self )
self.gui = PandaGUI( scriptPath, self )

def auth( self ):
user = self.settings.getSetting( "username" )
Expand Down Expand Up @@ -136,13 +132,9 @@ def cleanup( self ):
def quit( self ):
if self.gui != None:
self.gui.close()
self.die = True

if __name__ == '__main__':
if not ( os.path.exists( os.path.join( scriptPath, "crypt_key_input.h" ) ) \
and os.path.exists( os.path.join( scriptPath, "crypt_key_output.h" ) ) ):
xbmcgui.Dialog().ok( "Pandora", "Missing encription key files." )
dlg.close()
else:
panda = Panda()
panda.main()
dlg.close()
panda = Panda()
dlg.close()
panda.main()
21 changes: 2 additions & 19 deletions libpandora/crypt.py
@@ -1,19 +1,10 @@
import sys
import keys

mod = long(2) ** 32

def decryptString( strIn, key=keys.key_in ):
def decryptString( strIn, key ):

dec = strIn.decode( "hex" )
ret = []

# print "Decoded:"
# for s in dec:
# sys.stdout.write( "%02x" %ord(s) )
# sys.stdout.flush()
# print

for i in range( 0, len( dec ), 8 ):
l = ord( dec[ i ] ) << 24 | \
ord( dec[ i + 1 ] ) << 16 | \
Expand Down Expand Up @@ -60,7 +51,7 @@ def decryptString( strIn, key=keys.key_in ):

return "".join( ret )

def encryptString( inStr, key=keys.key_out ):
def encryptString( inStr, key ):
blocks = ( len( inStr ) / 8 ) + 1

#Pad with \0
Expand Down Expand Up @@ -117,11 +108,3 @@ def encryptString( inStr, key=keys.key_out ):
ret.append( chr( r & 0xff ) )

return "".join( ret ).encode( "hex" )



if __name__ == "__main__":
print "In:",
strIn = raw_input()

print encryptString( strIn )
104 changes: 99 additions & 5 deletions libpandora/keys.py
@@ -1,15 +1,109 @@
import urllib2
import pickle
import pianoparser
import os

class KeyFile:
def __init__( self, fname ):
BASE_KEY_URL = "https://raw.github.com/PromyLOPh/pianobar/master/src/libpiano/"

class Key:
def __init__( self, proto, key ):
self._key = {}
( n, p, s ) = pianoparser.parse_file( fname )
( n, p, s ) = key
self._proto = proto
self._key["n"] = n
self._key["p"] = p
self._key["s"] = s

def __getitem__( self, key ):
return self._key[key]

key_out = KeyFile( "crypt_key_output.h" )
key_in = KeyFile( "crypt_key_input.h" )
class Keys:
_keys = None

def __init__( self, dataDir, proto ):
self._proto = proto
self._dataDir = dataDir
self._keys = {}

def __getitem__( self, key ):
return self._keys[key]

def loadKeys( self, save=True ):
fromFile = 0
key_in = self._loadKeyFromFile( os.path.join( self._dataDir,\
"key_in" ) )
if key_in and key_in._proto == self._proto:
fromFile += 1
else:
key_in = self._loadKeyFromURL( BASE_KEY_URL + \
"crypt_key_input.h" )
if key_in:
key_in = Key( self._proto, key_in )
else:
return False

key_out = self._loadKeyFromFile( os.path.join( self._dataDir,\
"key_out" ) )
if key_out and key_out._proto == self._proto:
fromFile += 1
else:
key_out = self._loadKeyFromURL( BASE_KEY_URL + \
"crypt_key_output.h" )
if key_out:
key_out = Key( self._proto, key_out )
else:
return False

self._keys['in'] = key_in
self._keys['out'] = key_out
if save and fromFile < 2:
self.saveKeys()
return True

def saveKeys( self ):
print "PANDORA: Saving keys"
try:
f = open( os.path.join( self._dataDir, "key_in" ), "w" )
pickle.dump( self._keys['in'], f )
finally:
f.close()

try:
f = open( os.path.join( self._dataDir, "key_out" ), "w" )
pickle.dump( self._keys['out'], f )
finally:
f.close()

def forceReDownload( self ):
print "PANDORA: Forcing key ReDownload"
if os.path.exists( os.path.join( self._dataDir, "key_in" ) ):
os.remove( os.path.join( self._dataDir, "key_in" ) )
if os.path.exists( os.path.join( self._dataDir, "key_out" ) ):
os.remove( os.path.join( self._dataDir, "key_out" ) )
return self.loadKeys( True );

def _loadKeyFromFile( self, keyFile ):
print "PANDORA: Loading key from file \"%s\"" %keyFile
if not os.path.isfile( keyFile ):
return False

try:
try:
f = open( keyFile, "rb" )
key = pickle.load( f )
except:
return False
finally:
f.close()

return key

def _loadKeyFromURL( self, keyUrl ):
print "PANDORA: Downloading key from url \"%s\"" %keyUrl
try:
f = urllib2.urlopen( keyUrl )
except urllib2.URLError, w:
return False
key = pianoparser.parse_file( f )
f.close()
return key
24 changes: 13 additions & 11 deletions libpandora/pandora.py
Expand Up @@ -3,6 +3,7 @@
import time

import crypt
import keys

PROTOCOL_VERSION=30
BASE_URL = "http://www.pandora.com/radio/xmlrpc/v%d?" %PROTOCOL_VERSION
Expand All @@ -17,20 +18,20 @@ class Pandora:
lid = ""
authToken = ""
curStation = ""
curFormat = "mp3" #Default to mp3 if not specified
curFormat = ""

def __init__( self ):
def __init__( self, dataDir, fmt = "mp3" ):
self.dataDir = dataDir
self.rid = "%07i" %( time.time() % 10000000 )

def __init__( self, format ):
self.rid = "%07i" %( time.time() % 10000000 )
self.curFormat = format
self.keys = keys.Keys( self.dataDir, PROTOCOL_VERSION )
self.keys.loadKeys()
self.curFormat = fmt

def sync( self ):
reqUrl = BASE_URL_RID %( self.rid, "sync" )

req = xmlrpclib.dumps( (), "misc.sync" ).replace( "\n", "" )
enc = crypt.encryptString( req )
enc = crypt.encryptString( req, self.keys['out'] )

u = urlopen( reqUrl, enc )
resp = u.read()
Expand All @@ -42,7 +43,7 @@ def authListener( self, user, pwd ):
req = xmlrpclib.dumps( ( _inttime(), user, pwd ), \
"listener.authenticateListener" )
req = req.replace( "\n", "" )
enc = crypt.encryptString( req )
enc = crypt.encryptString( req, self.keys['out'] )

u = urlopen( reqUrl, enc )
resp = u.read()
Expand All @@ -66,7 +67,7 @@ def getStations( self ):
req = xmlrpclib.dumps( ( _inttime(), self.authToken ), \
"station.getStations" )
req = req.replace( "\n", "" )
enc = crypt.encryptString( req )
enc = crypt.encryptString( req, self.keys['out'] )

u = urlopen( reqUrl, enc )
resp = u.read()
Expand All @@ -87,7 +88,7 @@ def getFragment( self, stationId=None, format=None ):
format, "0", "0" )
req = xmlrpclib.dumps( args, "playlist.getFragment" )
req = req.replace( "\n", "" )
enc = crypt.encryptString( req )
enc = crypt.encryptString( req, self.keys['out'] )

u = urlopen( reqUrl, enc )
resp = u.read()
Expand All @@ -98,7 +99,8 @@ def getFragment( self, stationId=None, format=None ):
#last 48 chars of URL encrypted, padded w/ 8 * '\x08'
for i in range( len( parsed ) ):
url = parsed[i]["audioURL"]
url = url[:-48] + crypt.decryptString( url[-48:] )[:-8]
url = url[:-48] + crypt.decryptString( url[-48:],\
self.keys['in'] )[:-8]
parsed[i]["audioURL"] = url

self.curStation = stationId
Expand Down
4 changes: 1 addition & 3 deletions libpandora/pianoparser.py
@@ -1,9 +1,7 @@
import re
import os

def parse_file( fname ):
def parse_file( f ):

f = open( os.path.join( os.getcwd(), fname ), 'r' )
lines = f.readlines()
f.close()
lines = cleanup( lines )
Expand Down
5 changes: 3 additions & 2 deletions pandagui.py
Expand Up @@ -15,8 +15,9 @@
BTN_HIDE = 335

class PandaGUI(xbmcgui.WindowXMLDialog):
def __init__(self,strXMLname, strFallbackPath,strDefaultName,strRes,bforeFallback=0,panda=None):
xbmcgui.WindowXMLDialog.__init__( self, strXMLname, strFallbackPath, strDefaultName, strRes)
def __init__(self, scriptPath, panda):
xbmcgui.WindowXMLDialog.__init__( self, "script-pandora.xml", \
scriptPath, defaultSkin = "Default", defaultRes = "NTSC" )
self.panda = panda

def onInit(self):
Expand Down

0 comments on commit 2aaaf5e

Please sign in to comment.