Esempio n. 1
0
def __handleInternalRequest(pathNouns, _path, **kwargs):
    #
    # Handle a request internally
    #
    if len(pathNouns) > 1:
        if pathNouns[1] == "resources":
            if len(pathNouns) == 3:
                ext = pathNouns[2][pathNouns[2].rfind("."):]
                PMS.Log("(Framework) Getting resource named '%s'" %
                        pathNouns[2])
                resource = Resource.Load(pathNouns[2])
                return Objects.DataObject(resource,
                                          Resource.MimeTypeForExtension(ext))

        if pathNouns[1] == "sharedresources":
            if len(pathNouns) == 3:
                ext = pathNouns[2][pathNouns[2].rfind("."):]
                PMS.Log("(Framework) Getting shared resource named '%s'" %
                        pathNouns[2])
                resource = Resource.LoadShared(pathNouns[2])
                return Objects.DataObject(resource,
                                          Resource.MimeTypeForExtension(ext))

        elif pathNouns[1] == "function" and len(pathNouns) >= 3:
            name = pathNouns[2]
            pos = name.rfind(".")
            if pos > -1:
                name = name[:pos]
            PMS.Log(name)
            if name not in __reservedFunctionNames:
                if len(pathNouns) == 4:
                    kwargs['query'] = pathNouns[3]

                result = __callNamed(name, **kwargs)
                #PMS.Log(result)
                return result

        elif pathNouns[1] == "prefs":
            if len(pathNouns) == 2:
                return Prefs.__container()
            else:
                Prefs.__setAll(kwargs)
                result = __callNamed("ValidatePrefs", addToLog=False)
                if result:
                    return result
                else:
                    return ''
Esempio n. 2
0
def __handleInternalRequest(pathNouns, path, **kwargs):
    #
    # Handle a request internally
    #
    if len(pathNouns) > 1:
        if pathNouns[1] == "resources":
            if len(pathNouns) == 3:
                if Resource.__publicResources.has_key(pathNouns[2]):
                    PMS.Log("(Framework) Getting resource named '%s'" %
                            pathNouns[2])
                    resource = Resource.Load(pathNouns[2])
                    return Objects.DataObject(
                        resource, Resource.__publicResources[pathNouns[2]])

        if pathNouns[1] == "sharedresources":
            if len(pathNouns) == 3:
                if Resource.__publicSharedResources.has_key(pathNouns[2]):
                    PMS.Log("(Framework) Getting shared resource named '%s'" %
                            pathNouns[2])
                    resource = Resource.LoadShared(pathNouns[2])
                    return Objects.DataObject(
                        resource,
                        Resource.__publicSharedResources[pathNouns[2]])

        elif pathNouns[1] == "function" and len(pathNouns) >= 4:
            name = pathNouns[2]
            if name not in __reservedFunctionNames:
                encodedArgs = pathNouns[3]
                pos = encodedArgs.rfind(".")
                if pos > -1:
                    encodedArgs = encodedArgs[:pos]
                fkwargs = pickle.loads(D(encodedArgs))
                # Override encoded kwargs with kwargs passed in the URL
                for key in kwargs:
                    fkwargs[key] = kwargs[key]
                if len(pathNouns) == 4:
                    return __callNamed(name, **fkwargs)
                elif len(pathNouns) == 5:
                    return __callNamed(name, query=pathNouns[4], **fkwargs)

        elif pathNouns[1] == "prefs":
            if len(pathNouns) == 2:
                return Prefs.__container()
            else:
                Prefs.__setAll(kwargs)
                return __callNamed("ValidatePrefs", addToLog=False)
def __handleInternalRequest(pathNouns, _path, **kwargs):
  #
  # Handle a request internally
  #
  if len(pathNouns) > 1:
    if pathNouns[1] == "resources":
      if len(pathNouns) == 3:
        ext = pathNouns[2][pathNouns[2].rfind("."):]
        PMS.Log("(Framework) Getting resource named '%s'" % pathNouns[2])
        resource = Resource.Load(pathNouns[2])
        return Objects.DataObject(resource, Resource.MimeTypeForExtension(ext))

    if pathNouns[1] == "sharedresources":
      if len(pathNouns) == 3:
        ext = pathNouns[2][pathNouns[2].rfind("."):]
        PMS.Log("(Framework) Getting shared resource named '%s'" % pathNouns[2])
        resource = Resource.LoadShared(pathNouns[2])
        return Objects.DataObject(resource, Resource.MimeTypeForExtension(ext))
        
    elif pathNouns[1] == "function" and len(pathNouns) >= 3:
      name = pathNouns[2]
      pos = name.rfind(".")
      if pos > -1:
        name = name[:pos]
      PMS.Log(name)
      if name not in __reservedFunctionNames:
        if len(pathNouns) == 4:
          kwargs['query'] = pathNouns[3]
          
        result = __callNamed(name, **kwargs)
        #PMS.Log(result)
        return result
        
    elif pathNouns[1] == "prefs":
      if len(pathNouns) == 2:
        return Prefs.__container()
      else:
        Prefs.__setAll(kwargs)
        result = __callNamed("ValidatePrefs", addToLog=False)
        if result:
          return result
        else:
          return ''
Esempio n. 4
0
def __handleInternalRequest(pathNouns, count):
  #
  # Handle a request internally
  #
  global __publicResources
  if count > 1:
    if pathNouns[1] == "resources":
      if count == 3:
        if __publicResources.has_key(pathNouns[2]):
          Log.Add("Getting resource")
          resource = Resource(pathNouns[2])
          Response["Content-Type"] = __publicResources[pathNouns[2]]
          return resource

    elif pathNouns[1] == "prefs":
      return Prefs.__handleRequest(pathNouns,count)
def __handleInternalRequest(pathNouns, count):
  #
  # Handle a request internally
  #
  global __publicResources
  if count > 1:
    if pathNouns[1] == "resources":
      if count == 3:
        if __publicResources.has_key(pathNouns[2]):
          Log.Add("Getting resource")
          resource = Resource(pathNouns[2])
          Response["Content-Type"] = __publicResources[pathNouns[2]]
          return resource

    elif pathNouns[1] == "prefs":
      return Prefs.__handleRequest(pathNouns,count)
Esempio n. 6
0
def __run(_bundlePath):
    #
    # Initializes the framework, verifies the plug-in & extracts information, then enters a
    # run loop for handling requests.
    #
    global Identifier
    global Debug
    global __bundlePath
    global __pluginModule
    global __logFilePath
    global __requestHandlers
    global LastPrefix

    FirstRun = False
    random.seed()

    # Set up the support file paths
    pmsPath = "%s/Library/Application Support/Plex Media Server" % os.environ[
        "HOME"]
    supportFilesPath = "%s/Plug-in Support" % pmsPath
    frameworkSupportFilesPath = "%s/Framework Support" % pmsPath
    logFilesPath = "%s/Library/Logs/PMS Plugin Logs" % os.environ["HOME"]

    # Make sure framework directories exist
    def checkpath(path):
        try:
            if not os.path.exists(path): os.makedirs(path)
        except:
            pass

    checkpath("%s/Preferences" % supportFilesPath)
    checkpath("%s/Databases" % supportFilesPath)
    checkpath(logFilesPath)
    checkpath(frameworkSupportFilesPath)

    # Set the bundle path
    __bundlePath = _bundlePath.rstrip('/')

    # Add the bundle path to the system path, including any libraries
    if os.path.isdir("%s/Contents" % __bundlePath):
        sys.path.append("%s/Contents" % __bundlePath)
        if os.path.isdir("%s/Contents/Libraries" % __bundlePath):
            sys.path.append("%s/Contents/Libraries" % __bundlePath)
    else:
        print "Couldn't find bundle directory"
        return None

    # Open the Info.plist file
    f = open("%s/Contents/Info.plist" % __bundlePath, "r")
    infoplist = XML.ElementFromString(f.read())
    f.close()
    if infoplist is None:
        print "Couldn't load Info.plist file from plug-in"
        return

    # Get the plug-in identifier
    Identifier = infoplist.xpath(
        '//key[text()="CFBundleIdentifier"]//following-sibling::string/text()'
    )[0]
    if Identifier is None:
        print "Invalid Info.plist file in plug-in"
        return None

    # Set up the log file
    __logFilePath = "%s/%s.log" % (logFilesPath, Identifier)
    if os.path.exists(__logFilePath):
        if os.path.exists("%s.old" % __logFilePath):
            os.remove("%s.old" % __logFilePath)
        os.rename(__logFilePath, "%s.old" % __logFilePath)

    # Now we can start logging
    PMS.Log("(Framework) Bundle verification complete", False)

    # Check whether debugging is enabled
    try:
        _debug = infoplist.xpath(
            '//key[text()="PlexPluginDebug"]//following-sibling::string/text()'
        )[0]
        if _debug == "1":
            Debug = True
            PMS.Log("(Framework) Debugging is enabled")
    except:
        pass

    # Log the system encoding (set during bootstrap)
    PMS.Log("(Framework) Default encoding is " + sys.getdefaultencoding())

    # Set up framework paths
    Prefs.__prefsPath = "%s/Preferences/%s.xml" % (supportFilesPath,
                                                   Identifier)
    Data.__dataPath = "%s/Data/%s" % (supportFilesPath, Identifier)
    Data.__dataItemPath = "%s/DataItems" % Data.__dataPath
    if not os.path.isdir(Data.__dataItemPath):
        FirstRun = True
        os.makedirs(Data.__dataItemPath)
    Resource.__resourcePath = "%s/Contents/Resources" % __bundlePath
    Helper.__helperPath = "%s/Contents/Helpers" % __bundlePath
    Resource.__sharedResourcePath = "%s/Plug-ins/Framework.bundle/Contents/Resources/Versions/1/Resources" % pmsPath
    Database.__databasePath = "%s/Databases/%s.db" % (supportFilesPath,
                                                      Identifier)
    os.chdir(Data.__dataItemPath)
    Locale.SetDefaultLocale()
    PMS.Log("(Framework) Configured framework modules")

    # Attempt to import the plug-in module - if debugging is enabled, don't catch exceptions
    if Debug:
        import Code as _plugin
        PMS.Log("(Framework) Imported plug-in module")
    else:
        try:
            import Code as _plugin
            PMS.Log("(Framework) Imported plug-in module")
        except ImportError:
            PMS.Log("(Framework) Couldn't import plug-in from bundle")
            __exit()
            return

    # Load the list of trusted plug-ins
    _trusted = []
    try:
        _trustedJSON = Resource.LoadShared("trust.json")
        if _trustedJSON:
            _trusted = JSON.ObjectFromString(_trustedJSON)
    except:
        pass

    # Populate the permission lists
    __setupPermissionLists()

    # Register the plug-in with the framework
    __pluginModule = _plugin

    # Check the imported module to make sure nothing untoward is happening!
    if Identifier in _trusted:
        PMS.Log("(Framework) Plug-in is trusted, skipping module check")
    else:
        __scanModules()
        _allowed = []
        for n in PMS.__dict__:
            if n[0] != "_":
                if type(PMS.__dict__[n]).__name__ == "module":
                    _allowed.append(n)
        for n in __modWhitelist:
            _allowed.append(n)
        __checkModule(_plugin, _allowed)
        PMS.Log("(Framework) Checked module imports")

    # Initialize the framework modules
    Dict.__load()
    if not FirstRun:
        __checkFrameworkCompatibility()
    Prefs.__load()
    HTTP.__loadCookieJar()
    HTTP.__loadCache()
    PMS.Log("(Framework) Initialized framework modules")

    # Call the plug-in's Start method
    PMS.Log("(Framework) Attempting to start the plug-in...")
    __call(__pluginModule.Start)
    PMS.Log("(Framework) Plug-in started", False)

    # Start timers
    __startCacheManager(firstRun=FirstRun)

    PMS.Log("(Framework) Entering run loop")
    # Enter a run loop to handle requests
    while True:
        try:
            # Read the input
            path = raw_input()
            path = path.lstrip("GET ").strip()
            LastPrefix = None

            # Read headers
            headers = {}
            stop = False
            while stop == False:
                line = raw_input()
                if len(line) == 1:
                    stop = True
                else:
                    split = string.split(line.strip(), ":", maxsplit=1)
                    if len(split) == 2:
                        headers[split[0].strip()] = split[1].strip()

            # Set the locale
            if headers.has_key("X-Plex-Language"):
                loc = headers["X-Plex-Language"].lower()
                Locale.__loadLocale(loc)

            # Set the version
            if headers.has_key("X-Plex-Version"):
                Client.__setVersion(headers["X-Plex-Version"])

            # Extract arguments
            kwargs = {}
            mpath = path
            if path.find("?") >= 0:
                parts = path.split("?")
                mpath = parts[0]
                args = parts[1].split("&")
                for arg in args:
                    kwarg = arg.split("=")
                    if len(kwarg) == 2:
                        name = urllib.unquote(kwarg[0])
                        value = urllib.unquote(kwarg[1])
                        kwargs[name] = value
            if mpath[-1] == "/":
                mpath = mpath[:-1]

            # Split the path into components and decode.
            pathNouns = path.split('/')
            pathNouns = [urllib.unquote(p) for p in pathNouns]

            # If no input was given, return an error
            if len(pathNouns) <= 1:
                __return("%s\r\n\r\n" % PMS.Error['BadRequest'])

            # Otherwise, attempt to handle the request
            else:
                result = None
                pathNouns.pop(0)
                count = len(pathNouns)
                if pathNouns[-1] == "":
                    pathNouns.pop(len(pathNouns) - 1)
                PMS.Log("(Framework) Handling request :  %s" % path, False)

                # Check for a management request
                if pathNouns[0] == ":":
                    result = __handlePMSRequest(pathNouns, path, **kwargs)

                else:
                    handler = None
                    isPrefixHandler = False

                    # See if there's a prefix handler available
                    for key in __prefixHandlers:
                        if mpath.count(key, 0, len(key)) == 1:
                            LastPrefix = key
                    if mpath in __prefixHandlers:
                        handler = __prefixHandlers[mpath]["handler"]
                        isPrefixHandler = True

                    else:
                        # Check each request handler to see if it handles the current prefix
                        popped = False
                        for key in __requestHandlers:
                            if handler is None:
                                if path.count(key, 0, len(key)) == 1:
                                    # Remove the prefix from the path
                                    keyNounCount = len(key.split('/')) - 1
                                    for i in range(keyNounCount):
                                        pathNouns.pop(0)
                                    count = count - keyNounCount
                                    # Find the request handler
                                    handler = __requestHandlers[key]["handler"]
                                    LastPrefix = key
                                    popped = True

                        # If no path request handler was found, make sure we still pop the prefix so internal requests work
                        for key in __prefixHandlers:
                            if popped == False:
                                if mpath.count(key, 0, len(key)) == 1:
                                    keyNounCount = len(key.split('/')) - 1
                                    for i in range(keyNounCount):
                                        pathNouns.pop(0)
                                    popped = True

                    # Check whether we should handle the request internally
                    handled = False
                    if count > 0:
                        if pathNouns[0] == ":":
                            handled = True
                            result = __handleInternalRequest(
                                pathNouns, path, **kwargs)

                    # Check if the App Store has flagged the plug-in as broken
                    if os.path.exists(
                            os.path.join(frameworkSupportFilesPath,
                                         "%s.broken" % Identifier)):
                        #TODO: Localise this bit, use message from the App Store if available
                        handled = True
                        result = PMS.Objects.MessageContainer(
                            "Please try again later",
                            "This plug-in is currently unavailable")
                        PMS.Log("(Framework) Plug-in is flagged as broken")

                    # If the request hasn't been handled, and we have a valid request handler, call it
                    else:
                        if not handled and handler is not None:
                            if isPrefixHandler:
                                result = handler(**kwargs)
                            else:
                                result = handler(pathNouns, path, **kwargs)

                # If the request wasn't handled, return an error
                if result == None:
                    PMS.Log("(Framework) Request not handled by plug-in",
                            False)
                    response = "%s\r\n\r\n" % PMS.Error['NotFound']

                # If the plugin returned an error, return it to PMS
                elif result in PMS.Error.values():
                    PMS.Log(
                        "(Framework) Plug-in returned an error :  %s" % result,
                        False)
                    response = "%s\r\n" % result

                # Otherwise, check if a valid object was returned, and return the result
                elif __objectManager.ObjectHasBase(result, Objects.Object):
                    PMS.Log("(Framework) Response OK")
                    resultStr = result.Content()
                    resultStatus = result.Status()
                    resultHeaders = result.Headers()
                    if resultStr is not None:
                        resultLen = len(resultStr)
                        if resultLen > 0:
                            resultHeaders += "Content-Length: %i\r\n" % resultLen
                        resultStr = "\r\n%s" % resultStr
                    else:
                        resultStr = ""
                    response = str("%s\r\n%s" %
                                   (resultStatus, resultHeaders)) + str(
                                       resultStr) + str("\r\n")

                __return(response)

        # If a KeyboardInterrupt (SIGINT) is raised, stop the plugin
        except KeyboardInterrupt:
            # Save data & exit
            __saveData()
            __exit()

        except EOFError:
            # Save data & exit
            __saveData()
            __exit()

        # If another exception is raised, deal with the problem
        except:
            __except()
            __return("%s\r\n\r\n" % PMS.Error['InternalError'])

        # Make sure the plugin's data is saved
        finally:
            __saveData()
def __run(_bundlePath):
  #
  # Initializes the framework, verifies the plug-in & extracts information, then enters a
  # run loop for handling requests. 
  #
  global Identifier
  global Debug
  global __bundlePath  
  global __pluginModule
  global __logFilePath
  global __requestHandlers
  global LastPrefix
  
  FirstRun = False
  random.seed()
  
  # Set up the support file paths
  if sys.platform == "win32":
    if 'PLEXLOCALAPPDATA' in os.environ:
      key = 'PLEXLOCALAPPDATA'
    else:
      key = 'LOCALAPPDATA'
    pmsPath = os.path.join(os.environ[key], 'Plex Media Server')
    logFilesPath = os.path.join(os.environ[key], "Plex Media Server", "Logs", "PMS Plugin Logs")
  else:
    pmsPath = "%s/Library/Application Support/Plex Media Server" % os.environ["HOME"]
    logFilesPath = "%s/Library/Logs/PMS Plugin Logs" % os.environ["HOME"]
  
  supportFilesPath = "%s/Plug-in Support" % pmsPath
  frameworkSupportFilesPath = "%s/Framework Support" % pmsPath
  
  # Make sure framework directories exist
  def checkpath(path):
    try:
      if not os.path.exists(path): os.makedirs(path)
    except:
      pass
  
  checkpath("%s/Preferences" % supportFilesPath)
  checkpath("%s/Databases" % supportFilesPath)
  checkpath(logFilesPath)
  checkpath(frameworkSupportFilesPath)
    
  # Set the bundle path
  __bundlePath = _bundlePath.rstrip('/')
  
  # Add the bundle path to the system path, including any libraries
  if os.path.isdir("%s/Contents" % __bundlePath):
    sys.path.append("%s/Contents" % __bundlePath)
    if os.path.isdir("%s/Contents/Libraries" % __bundlePath):
      sys.path.append("%s/Contents/Libraries" % __bundlePath)
  else:
    print "Couldn't find bundle directory"
    return None
  
  # Open the Info.plist file
  f = open("%s/Contents/Info.plist" % __bundlePath, "r")
  infoplist = XML.ElementFromString(f.read())
  f.close()
  if infoplist is None:
    print "Couldn't load Info.plist file from plug-in"
    return

  # Get the plug-in identifier
  Identifier = infoplist.xpath('//key[text()="CFBundleIdentifier"]//following-sibling::string/text()')[0]
  if Identifier is None:
    print "Invalid Info.plist file in plug-in"
    return None
    
  # Set up the log file
  __logFilePath = "%s/%s.log" % (logFilesPath, Identifier)
  if os.path.exists(__logFilePath):
    if os.path.exists("%s.old" % __logFilePath):
      os.remove("%s.old" % __logFilePath)
    os.rename(__logFilePath, "%s.old" % __logFilePath)
  
  # Now we can start logging
  PMS.Log("(Framework) Bundle verification complete", False)
  
  # Check whether debugging is enabled
  try:
    _debug = infoplist.xpath('//key[text()="PlexPluginDebug"]//following-sibling::string/text()')[0]
    if _debug == "1":
      Debug = True
      PMS.Log("(Framework) Debugging is enabled")
  except: pass

  # Log the system encoding (set during bootstrap)
  PMS.Log("(Framework) Default encoding is " + sys.getdefaultencoding())

  # Set up framework paths
  Prefs.__prefsPath = "%s/Preferences/%s.xml" % (supportFilesPath, Identifier)
  Data.__dataPath = "%s/Data/%s" % (supportFilesPath, Identifier)
  Data.__dataItemPath = "%s/DataItems" % Data.__dataPath
  if not os.path.isdir(Data.__dataItemPath):
    FirstRun = True
    os.makedirs(Data.__dataItemPath)
  Resource.__resourcePath = "%s/Contents/Resources" % __bundlePath
  Helper.__helperPath = "%s/Contents/Helpers" % __bundlePath
  Resource.__sharedResourcePath = "%s/Plug-ins/Framework.bundle/Contents/Resources/Versions/1/Resources" % pmsPath
  Database.__databasePath = "%s/Databases/%s.db" % (supportFilesPath, Identifier)
  os.chdir(Data.__dataItemPath)
  Locale.SetDefaultLocale()
  PMS.Log("(Framework) Configured framework modules")

  # Attempt to import the plug-in module - if debugging is enabled, don't catch exceptions
  if Debug:
    import Code as _plugin
    PMS.Log("(Framework) Imported plug-in module")
  else:
    try:
      import Code as _plugin
      PMS.Log("(Framework) Imported plug-in module")
    except ImportError:
      PMS.Log("(Framework) Couldn't import plug-in from bundle")
      __exit()
      return
      
  # Load the list of trusted plug-ins
  _trusted = []
  try:
    _trustedJSON = Resource.LoadShared("trust.json")
    if _trustedJSON:
      _trusted = JSON.ObjectFromString(_trustedJSON)
  except:
    pass

  # Populate the permission lists
  __setupPermissionLists()

  # Register the plug-in with the framework
  __pluginModule = _plugin

  # Check the imported module to make sure nothing untoward is happening!
  if Identifier in _trusted:
    PMS.Log("(Framework) Plug-in is trusted, skipping module check")
  else:
    __scanModules()
    _allowed = []
    for n in PMS.__dict__:
      if n[0] != "_":
        if type(PMS.__dict__[n]).__name__ == "module":
          _allowed.append(n)
    for n in __modWhitelist:
      _allowed.append(n)
    __checkModule(_plugin, _allowed)
    PMS.Log("(Framework) Checked module imports")

  # Initialize the framework modules
  Dict.__load()
  if not FirstRun:
    __checkFrameworkCompatibility()
  Prefs.__load()
  HTTP.__loadCookieJar()
  HTTP.__loadCache()
  if HTTP_TIMEOUT_VAR_NAME in os.environ:
    HTTP.SetTimeout(float(os.environ[HTTP_TIMEOUT_VAR_NAME]))
  else:
    HTTP.SetTimeout(HTTP_DEFAULT_TIMEOUT)
  PMS.Log("(Framework) Initialized framework modules")

  # Call the plug-in's Start method
  PMS.Log("(Framework) Attempting to start the plug-in...")
  __call(__pluginModule.Start)
  PMS.Log("(Framework) Plug-in started", False)

  # Start timers
  __startCacheManager(firstRun=FirstRun)
  
  PMS.Log("(Framework) Entering run loop")
  # Enter a run loop to handle requests
  while True:
    try:
      # Read the input
      path = raw_input()
      path = path.lstrip("GET ").strip()
      LastPrefix = None
      
      # Read headers
      headers = {}
      stop = False
      while stop == False:
        line = raw_input()
        if len(line) == 1:
          stop = True
        else:
          split = string.split(line.strip(), ":", maxsplit=1)
          if len(split) == 2:
            headers[split[0].strip()] = split[1].strip()

      # Set the locale
      if headers.has_key("X-Plex-Language"):
        loc = headers["X-Plex-Language"].lower()
        Locale.__loadLocale(loc)
        
      # Set the version
      if headers.has_key("X-Plex-Version"):
        Client.__setVersion(headers["X-Plex-Version"])

      PMS.Request.Headers.clear()
      PMS.Request.Headers.update(headers)
      
      req = urlparse.urlparse(path)
      path = req.path
      qs_args = cgi.parse_qs(req.query)
      kwargs = {}
      if 'function_args' in qs_args:
        try:
          unquoted_args = urllib.unquote(qs_args['function_args'][0])
          decoded_args = PMS.String.Decode(unquoted_args)
          kwargs = cerealizer.loads(decoded_args)
        except:
          PMS.Log(PMS.Plugin.Traceback())
          raise Exception("Unable to deserialize arguments")
      
      for arg_name in qs_args:
        if arg_name != 'function_args':
          kwargs[arg_name] = qs_args[arg_name][0]
      
      # First, try to match a connected route
      rpath = path
      for key in __prefixHandlers:
        if rpath.count(key, 0, len(key)) == 1:
          rpath = rpath[len(key):]
          break
      f, route_kwargs = MatchRoute(rpath)
      
      if f is not None:
        PMS.Log("(Framework) Handling route request :  %s" % path, False)
        route_kwargs.update(kwargs)
        result = f(**route_kwargs)

      # No route, fall back to other path handling methods
      else:
        mpath = path
        
        if mpath[-1] == "/":
          mpath = mpath[:-1]
        
        # Split the path into components and decode.
        pathNouns = path.split('/')
        pathNouns = [urllib.unquote(p) for p in pathNouns]
      
        # If no input was given, return an error
        if len(pathNouns) <= 1:
          __return("%s\r\n\r\n" % PMS.Error['BadRequest'])
        
        # Otherwise, attempt to handle the request
        else:
          result = None
          pathNouns.pop(0)
          count = len(pathNouns)
          if pathNouns[-1] == "":
            pathNouns.pop(len(pathNouns)-1)
          PMS.Log("(Framework) Handling request :  %s" % path, False)
        
          # Check for a management request
          if pathNouns[0] == ":":
            result = __handlePMSRequest(pathNouns, path, **kwargs)

          else:  
            handler = None
            isPrefixHandler = False
          
            # See if there's a prefix handler available
            for key in __prefixHandlers:
              if mpath.count(key, 0, len(key)) == 1:
                LastPrefix = key
            if mpath in __prefixHandlers:
              handler = __prefixHandlers[mpath]["handler"]
              isPrefixHandler = True
            
            else:
              # Check each request handler to see if it handles the current prefix
              popped = False
              for key in __requestHandlers:
                if handler is None:
                  if path.count(key, 0, len(key)) == 1:
                    # Remove the prefix from the path
                    keyNounCount = len(key.split('/')) - 1
                    for i in range(keyNounCount):
                      pathNouns.pop(0)
                    count = count - keyNounCount
                    # Find the request handler
                    handler = __requestHandlers[key]["handler"]
                    LastPrefix = key
                    popped = True
            
              # If no path request handler was found, make sure we still pop the prefix so internal requests work
              for key in __prefixHandlers:
                if popped == False:
                  if mpath.count(key, 0, len(key)) == 1:
                    keyNounCount = len(key.split('/')) - 1
                    for i in range(keyNounCount):
                      pathNouns.pop(0)
                    popped = True

            # Check whether we should handle the request internally
            handled = False
            if count > 0:
              if pathNouns[0] == ":":
                handled = True
                result = __handleInternalRequest(pathNouns, path, **kwargs)
    
          
            # Check if the App Store has flagged the plug-in as broken
            if os.path.exists(os.path.join(frameworkSupportFilesPath, "%s.broken" % Identifier)):
              #TODO: Localise this bit, use message from the App Store if available
              handled = True
              result = PMS.Objects.MessageContainer("Please try again later", "This plug-in is currently unavailable")
              PMS.Log("(Framework) Plug-in is flagged as broken")
          
            # If the request hasn't been handled, and we have a valid request handler, call it
            else:
              if not handled and handler is not None:
                if isPrefixHandler:
                  result = handler(**kwargs)
                else:
                  result = handler(pathNouns, path, **kwargs)
        
      response = None
      
      # If the request wasn't handled, return an error
      if result == None:
        PMS.Log("(Framework) Request not handled by plug-in", False)
        response = "%s\r\n\r\n" % PMS.Error['NotFound']
        
      # If the plugin returned an error, return it to PMS
      elif result in PMS.Error.values():
        PMS.Log("(Framework) Plug-in returned an error :  %s" % result, False)
        response = "%s\r\n\r\n" % result
        
      # Otherwise, check if a valid object was returned, and return the result
      elif __objectManager.ObjectHasBase(result, Objects.Object):
        resultStr = result.Content()
        resultStatus = result.Status()
        resultHeaders = result.Headers()
      
      elif isinstance(result, basestring):
        resultStr = result
        resultStatus = '200 OK'
        resultHeaders = "Content-type: text/plain\r\n"
      
      if resultStr is not None:
        PMS.Log("(Framework) Response OK")
        resultLen = len(resultStr)
        if resultLen > 0:
          resultHeaders += "Content-Length: %i\r\n" % resultLen
          resultStr = "\r\n"+resultStr
        
      else:
        resultStr = ""
        resultStatus = '500 Internal Server Error'
        resultHeaders = ''
        PMS.Log("(Framework) Unknown response type")
        
      if response == None:
        response = str("%s\r\n%s" % (resultStatus, resultHeaders)) + str(resultStr) + str("\r\n")
      
      #PMS.Log("\n---\n"+response+"---\n")
        
      __return(response)
    
    # If a KeyboardInterrupt (SIGINT) is raised, stop the plugin
    except KeyboardInterrupt:
      # Save data & exit
      __saveData()
      __exit()     
    
    except EOFError:
      # Save data & exit
      __saveData()
      __exit()
          
    # If another exception is raised, deal with the problem
    except:
      __except()
      __return("%s\r\n\r\n" % PMS.Error['InternalError'])
    
    # Make sure the plugin's data is saved
    finally:
      __saveData()