コード例 #1
0
 def testReadPlistCanParseXMLPlistAtOffset(self):
     for _ in range(5):
         self.min_xml.seek(0, os.SEEK_SET)
         rand_int = random.randint(0, 2048)
         padding = "A" * rand_int
         padded_xml_plist = padding + self.min_xml.read()
         fd = StringIO.StringIO(padded_xml_plist)
         fd.seek(rand_int, os.SEEK_SET)
         binplist.readPlist(fd)
コード例 #2
0
ファイル: binplist_test.py プロジェクト: google/binplist
 def testReadPlistCanParseXMLPlistAtOffset(self):
   for _ in range(5):
     self.min_xml.seek(0, os.SEEK_SET)
     rand_int = random.randint(0, 2048)
     padding = "A" * rand_int
     padded_xml_plist = padding + self.min_xml.read()
     fd = StringIO.StringIO(padded_xml_plist)
     fd.seek(rand_int, os.SEEK_SET)
     binplist.readPlist(fd)
コード例 #3
0
ファイル: binplist_test.py プロジェクト: google/binplist
 def testReadPlist(self):
   blank_file = StringIO.StringIO()
   self.assertRaises(binplist.FormatError, binplist.readPlist, blank_file)
   bplist15 = StringIO.StringIO("bplist15")
   self.assertRaises(binplist.FormatError, binplist.readPlist, bplist15)
   # Incomplete XML does not parse (expat error from plistlib is caught)
   xml_file = StringIO.StringIO("<xml")
   self.assertRaises(binplist.FormatError, binplist.readPlist, xml_file)
   # Check that a minimal XML plist does parse.
   # We're not testing full XML parsing per se as that's plistlib's tests job.
   binplist.readPlist(self.min_xml)
コード例 #4
0
 def testReadPlist(self):
     blank_file = StringIO.StringIO()
     self.assertRaises(binplist.FormatError, binplist.readPlist, blank_file)
     bplist15 = StringIO.StringIO("bplist15")
     self.assertRaises(binplist.FormatError, binplist.readPlist, bplist15)
     # Incomplete XML does not parse (expat error from plistlib is caught)
     xml_file = StringIO.StringIO("<xml")
     self.assertRaises(binplist.FormatError, binplist.readPlist, xml_file)
     # Check that a minimal XML plist does parse.
     # We're not testing full XML parsing per se as that's plistlib's tests job.
     binplist.readPlist(self.min_xml)
コード例 #5
0
  def Run(self, args):
    self.context = args.context
    self.filter_query = args.query

    with vfs.VFSOpen(args.pathspec, progress_callback=self.Progress) as fd:
      data = fd.Read(self.MAX_PLIST_SIZE)
      plist = binplist.readPlist(cStringIO.StringIO(data))

      # Create the query parser
      parser = plist_lib.PlistFilterParser(self.filter_query).Parse()
      filter_imp = plist_lib.PlistFilterImplementation
      matcher = parser.Compile(filter_imp)

      if self.context:
        # Obtain the values for the context using the value expander
        value_expander = filter_imp.FILTERS["ValueExpander"]
        iterator = value_expander().Expand(plist, self.context)
      else:
        # If we didn't get a context, the context is the whole plist
        iterator = [plist]

      reply = protodict.RDFValueArray()
      for item in iterator:
        # As we're setting the context manually, we need to account for types
        if isinstance(item, types.ListType):
          for sub_item in item:
            partial_plist = plist_lib.PlistValueToPlainValue(sub_item)
            if matcher.Matches(partial_plist):
              reply.Append(sub_item)
        else:
          partial_plist = plist_lib.PlistValueToPlainValue(item)
          if matcher.Matches(partial_plist):
            reply.Append(partial_plist)
      self.SendReply(reply)
コード例 #6
0
ファイル: plist.py プロジェクト: timevortex/grr
  def Run(self, args):
    self.context = args.context
    self.filter_query = args.query

    with vfs.VFSOpen(args.pathspec, progress_callback=self.Progress) as fd:
      data = fd.Read(self.MAX_PLIST_SIZE)
      plist = binplist.readPlist(cStringIO.StringIO(data))

      # Create the query parser
      parser = plist_lib.PlistFilterParser(self.filter_query).Parse()
      filter_imp = plist_lib.PlistFilterImplementation
      matcher = parser.Compile(filter_imp)

      if self.context:
        # Obtain the values for the context using the value expander
        value_expander = filter_imp.FILTERS["ValueExpander"]
        iterator = value_expander().Expand(plist, self.context)
      else:
        # If we didn't get a context, the context is the whole plist
        iterator = [plist]

      reply = rdfvalue.RDFValueArray()
      for item in iterator:
        # As we're setting the context manually, we need to account for types
        if isinstance(item, types.ListType):
          for sub_item in item:
            partial_plist = plist_lib.PlistValueToPlainValue(sub_item)
            if matcher.Matches(partial_plist):
              reply.Append(sub_item)
        else:
          partial_plist = plist_lib.PlistValueToPlainValue(item)
          if matcher.Matches(partial_plist):
            reply.Append(partial_plist)
      self.SendReply(reply)
コード例 #7
0
    def GetTopLevel(self, file_object, file_name=u''):
        """Returns the deserialized content of a plist as a dictionary object.

    Args:
      file_object: A file-like object to parse.
      file_name: The name of the file-like object.

    Returns:
      A dictionary object representing the contents of the plist.

    Raises:
      UnableToParseFile: when the file cannot be parsed.
    """
        # Note that binplist.readPlist does not seek to offset 0.
        try:
            top_level_object = binplist.readPlist(file_object)

        except binplist.FormatError as exception:
            if not isinstance(exception, py2to3.BYTES_TYPE):
                error_string = str(exception).decode(u'utf8',
                                                     errors=u'replace')
            else:
                error_string = exception

            raise errors.UnableToParseFile(
                u'File is not a plist file: {0:s}'.format(error_string))

        except (AttributeError, LookupError, ValueError,
                binascii.Error) as exception:
            raise errors.UnableToParseFile(
                u'Unable to parse XML file, reason: {0:s}'.format(exception))

        except OverflowError as exception:
            raise errors.UnableToParseFile(
                u'Unable to parse: {0:s} with error: {1:s}'.format(
                    file_name, exception))

        if not top_level_object:
            raise errors.UnableToParseFile(
                u'File is not a plist: missing top level object')

        # Since we are using readPlist from binplist now instead of manually
        # opening  the binary plist file we loose this option. Keep it commented
        # out for now but this needs to be tested a bit more.
        # TODO: Re-evaluate if we can delete this or still require it.
        #if bpl.is_corrupt:
        #  logging.warning(
        #      u'Corruption detected in binary plist: {0:s}'.format(file_name))

        return top_level_object
コード例 #8
0
ファイル: plist.py プロジェクト: rgayon/plaso
  def GetTopLevel(self, file_object, file_name=u''):
    """Returns the deserialized content of a plist as a dictionary object.

    Args:
      file_object: A file-like object to parse.
      file_name: The name of the file-like object.

    Returns:
      A dictionary object representing the contents of the plist.

    Raises:
      UnableToParseFile: when the file cannot be parsed.
    """
    # Note that binplist.readPlist does not seek to offset 0.
    try:
      top_level_object = binplist.readPlist(file_object)

    except binplist.FormatError as exception:
      if not isinstance(exception, py2to3.BYTES_TYPE):
        error_string = str(exception).decode(u'utf8', errors=u'replace')
      else:
        error_string = exception

      raise errors.UnableToParseFile(
          u'File is not a plist file: {0:s}'.format(error_string))

    except (
        AttributeError, LookupError, ValueError, binascii.Error) as exception:
      raise errors.UnableToParseFile(
          u'Unable to parse XML file, reason: {0:s}'.format(exception))

    except OverflowError as exception:
      raise errors.UnableToParseFile(
          u'Unable to parse: {0:s} with error: {1:s}'.format(
              file_name, exception))

    if not top_level_object:
      raise errors.UnableToParseFile(
          u'File is not a plist: missing top level object')

    # Since we are using readPlist from binplist now instead of manually
    # opening  the binary plist file we loose this option. Keep it commented
    # out for now but this needs to be tested a bit more.
    # TODO: Re-evaluate if we can delete this or still require it.
    #if bpl.is_corrupt:
    #  logging.warning(
    #      u'Corruption detected in binary plist: {0:s}'.format(file_name))

    return top_level_object
コード例 #9
0
  def Parse(self, cmd, args, stdout, stderr, return_val, time_taken,
            knowledge_base):
    """Parse the system profiler output. We get it in the form of a plist."""
    _ = stderr, time_taken, args, knowledge_base  # Unused
    self.CheckReturn(cmd, return_val)

    serial_number = []
    hardware_list = []
    plist = binplist.readPlist(cStringIO.StringIO(stdout))

    if len(plist) > 1:
      raise parsers.ParseError("SPHardwareDataType plist has too many items.")

    hardware_list = plist[0]["_items"][0]
    serial_number = hardware_list["serial_number"]

    yield rdf_client.HardwareInfo(serial_number=serial_number)
コード例 #10
0
ファイル: osx_file_parser.py プロジェクト: timevortex/grr
  def Parse(self, cmd, args, stdout, stderr, return_val, time_taken,
            knowledge_base):
    """Parse the system profiler output. We get it in the form of a plist."""
    _ = stderr, time_taken, args, knowledge_base  # Unused
    self.CheckReturn(cmd, return_val)

    serial_number = []
    hardware_list = []
    plist = binplist.readPlist(cStringIO.StringIO(stdout))

    if len(plist) > 1:
      raise parsers.ParseError("SPHardwareDataType plist has too many items.")

    hardware_list = plist[0]["_items"][0]
    serial_number = hardware_list["serial_number"]

    yield rdfvalue.HardwareInfo(serial_number=serial_number)
コード例 #11
0
    def Read(self, file_object):
        """Reads a plist from a file-like object.

    Args:
      file_object: the file-like object.

    Raises:
      IOError: if the plist file-like object cannot be read.
    """
        try:
            file_object.seek(0, os.SEEK_SET)
            # Note that binplist.readPlist does not seek to offset 0.
            self.root_key = binplist.readPlist(file_object)

        except (AttributeError, binascii.Error, binplist.FormatError,
                LookupError, OverflowError, ValueError) as exception:
            raise IOError(exception)
コード例 #12
0
ファイル: plist.py プロジェクト: CNR-ITTIG/plasodfaxp
  def Read(self, file_object):
    """Reads a plist from a file-like object.

    Args:
      file_object: the file-like object.

    Raises:
      IOError: if the plist file-like object cannot be read.
    """
    try:
      file_object.seek(0, os.SEEK_SET)
      # Note that binplist.readPlist does not seek to offset 0.
      self.root_key = binplist.readPlist(file_object)

    except (
        AttributeError, binascii.Error, binplist.FormatError, LookupError,
        OverflowError, ValueError) as exception:
      raise IOError(exception)
コード例 #13
0
    def Parse(self, statentry, file_object, knowledge_base):
        """Parse the Plist file."""

        plist = binplist.readPlist(file_object)

        if not isinstance(plist, list):
            raise parser.ParseError(
                "InstallHistory plist is a '%s', expecting a list" %
                type(plist))

        for sw in plist:
            yield rdf_client.SoftwarePackage(
                name=sw.get("displayName"),
                version=sw.get("displayVersion"),
                description=",".join(sw.get("packageIdentifiers")),
                # TODO(hanuszczak): make installed_on an RDFDatetime
                installed_on=_DateToEpoch(sw.get("date")),
                install_state=rdf_client.SoftwarePackage.InstallState.INSTALLED
            )
コード例 #14
0
ファイル: plist.py プロジェクト: CNR-ITTIG/plasodfaxp
  def GetTopLevel(self, file_object, file_name=u''):
    """Returns the deserialized content of a plist as a dictionary object.

    Args:
      file_object: A file-like object to parse.
      file_name: The name of the file-like object.

    Returns:
      A dictionary object representing the contents of the plist.
    """
    # Note that binplist.readPlist does not seek to offset 0.
    try:
      top_level_object = binplist.readPlist(file_object)
    except binplist.FormatError as exception:
      raise errors.UnableToParseFile(
          u'[{0:s}] File is not a plist file: {1:s}'.format(
              self.NAME, utils.GetUnicodeString(exception)))
    except (
        LookupError, binascii.Error, ValueError, AttributeError) as exception:
      raise errors.UnableToParseFile(
          u'[{0:s}] Unable to parse XML file, reason: {1:s}'.format(
              self.NAME, exception))
    except OverflowError as exception:
      raise errors.UnableToParseFile(
          u'[{0:s}] Unable to parse: {1:s} with error: {2:s}'.format(
              self.NAME, file_name, exception))

    if not top_level_object:
      raise errors.UnableToParseFile(
          u'[{0:s}] File is not a plist: {1:s}'.format(
              self.NAME, utils.GetUnicodeString(file_name)))

    # Since we are using readPlist from binplist now instead of manually
    # opening  the binary plist file we loose this option. Keep it commented
    # out for now but this needs to be tested a bit more.
    # TODO: Re-evaluate if we can delete this or still require it.
    #if bpl.is_corrupt:
    #  logging.warning(
    #      u'[{0:s}] corruption detected in binary plist: {1:s}'.format(
    #          self.NAME, file_name))

    return top_level_object
コード例 #15
0
ファイル: plist.py プロジェクト: vonnopsled/plaso
    def GetTopLevel(self, file_object, file_name=u''):
        """Returns the deserialized content of a plist as a dictionary object.

    Args:
      file_object: A file-like object to parse.
      file_name: The name of the file-like object.

    Returns:
      A dictionary object representing the contents of the plist.
    """
        # Note that binplist.readPlist does not seek to offset 0.
        try:
            top_level_object = binplist.readPlist(file_object)
        except binplist.FormatError as exception:
            raise errors.UnableToParseFile(
                u'[{0:s}] File is not a plist file: {1:s}'.format(
                    self.NAME, utils.GetUnicodeString(exception)))
        except (LookupError, binascii.Error, ValueError,
                AttributeError) as exception:
            raise errors.UnableToParseFile(
                u'[{0:s}] Unable to parse XML file, reason: {1:s}'.format(
                    self.NAME, exception))
        except OverflowError as exception:
            raise errors.UnableToParseFile(
                u'[{0:s}] Unable to parse: {1:s} with error: {2:s}'.format(
                    self.NAME, file_name, exception))

        if not top_level_object:
            raise errors.UnableToParseFile(
                u'[{0:s}] File is not a plist: {1:s}'.format(
                    self.NAME, utils.GetUnicodeString(file_name)))

        # Since we are using readPlist from binplist now instead of manually
        # opening  the binary plist file we loose this option. Keep it commented
        # out for now but this needs to be tested a bit more.
        # TODO: Re-evaluate if we can delete this or still require it.
        #if bpl.is_corrupt:
        #  logging.warning(
        #      u'[{0:s}] corruption detected in binary plist: {1:s}'.format(
        #          self.NAME, file_name))

        return top_level_object
コード例 #16
0
    def Parse(self, cmd, args, stdout, stderr, return_val, time_taken,
              knowledge_base):
        """Parse the system profiler output. We get it in the form of a plist."""
        _ = stderr, time_taken, args, knowledge_base  # Unused
        self.CheckReturn(cmd, return_val)

        plist = binplist.readPlist(io.BytesIO(stdout))

        if len(plist) > 1:
            raise parser.ParseError(
                "SPHardwareDataType plist has too many items.")

        hardware_list = plist[0]["_items"][0]
        serial_number = getattr(hardware_list, "serial_number", None)
        system_product_name = getattr(hardware_list, "machine_model", None)
        bios_version = getattr(hardware_list, "boot_rom_version", None)

        yield rdf_client.HardwareInfo(serial_number=serial_number,
                                      bios_version=bios_version,
                                      system_product_name=system_product_name)
コード例 #17
0
ファイル: osx_file_parser.py プロジェクト: bhyvex/grr
  def Parse(self, cmd, args, stdout, stderr, return_val, time_taken,
            knowledge_base):
    """Parse the system profiler output. We get it in the form of a plist."""
    _ = stderr, time_taken, args, knowledge_base  # Unused
    self.CheckReturn(cmd, return_val)

    plist = binplist.readPlist(io.BytesIO(stdout))

    if len(plist) > 1:
      raise parser.ParseError("SPHardwareDataType plist has too many items.")

    hardware_list = plist[0]["_items"][0]
    serial_number = getattr(hardware_list, "serial_number", None)
    system_product_name = getattr(hardware_list, "machine_model", None)
    bios_version = getattr(hardware_list, "boot_rom_version", None)

    yield rdf_client.HardwareInfo(
        serial_number=serial_number,
        bios_version=bios_version,
        system_product_name=system_product_name)
コード例 #18
0
ファイル: osx_file_parser.py プロジェクト: bhyvex/grr
  def Parse(self, statentry, file_object, knowledge_base):
    """Parse the Plist file."""
    _ = knowledge_base
    kwargs = {}
    try:
      kwargs["aff4path"] = file_object.urn
    except AttributeError:
      pass

    direct_copy_items = [
        "Label", "Disabled", "UserName", "GroupName", "Program",
        "StandardInPath", "StandardOutPath", "StandardErrorPath",
        "LimitLoadToSessionType", "EnableGlobbing", "EnableTransactions",
        "OnDemand", "RunAtLoad", "RootDirectory", "WorkingDirectory", "Umask",
        "TimeOut", "ExitTimeOut", "ThrottleInterval", "InitGroups",
        "StartOnMount", "StartInterval", "Debug", "WaitForDebugger", "Nice",
        "ProcessType", "AbandonProcessGroup", "LowPriorityIO", "LaunchOnlyOnce"
    ]

    string_array_items = [
        "LimitLoadToHosts", "LimitLoadFromHosts", "LimitLoadToSessionType",
        "ProgramArguments", "WatchPaths", "QueueDirectories"
    ]

    flag_only_items = ["SoftResourceLimits", "HardResourceLimits", "Sockets"]

    plist = {}

    try:
      plist = binplist.readPlist(file_object)
    except (binplist.FormatError, ValueError, IOError) as e:
      plist["Label"] = "Could not parse plist: %s" % e

    # These are items that can be directly copied
    for key in direct_copy_items:
      kwargs[key] = plist.get(key)

    # These could be a string, they could be an array, we don't know and neither
    # does Apple so we check.
    for key in string_array_items:
      elements = plist.get(key)
      if isinstance(elements, basestring):
        kwargs[key] = [elements]
      else:
        kwargs[key] = elements

    # These are complex items that can appear in multiple data structures
    # so we only flag on their existence
    for key in flag_only_items:
      if plist.get(key):
        kwargs[key] = True

    if plist.get("inetdCompatability") is not None:
      kwargs["inetdCompatabilityWait"] = plist.get("inetdCompatability").get(
          "Wait")

    keepalive = plist.get("KeepAlive")
    if isinstance(keepalive, bool) or keepalive is None:
      kwargs["KeepAlive"] = keepalive
    else:
      keepalivedict = {}
      keepalivedict["SuccessfulExit"] = keepalive.get("SuccessfulExit")
      keepalivedict["NetworkState"] = keepalive.get("NetworkState")

      pathstates = keepalive.get("PathState")
      if pathstates is not None:
        keepalivedict["PathState"] = []
        for pathstate in pathstates:
          keepalivedict["PathState"].append(
              rdf_plist.PlistBoolDictEntry(
                  name=pathstate, value=pathstates[pathstate]))

      otherjobs = keepalive.get("OtherJobEnabled")
      if otherjobs is not None:
        keepalivedict["OtherJobEnabled"] = []
        for otherjob in otherjobs:
          keepalivedict["OtherJobEnabled"].append(
              rdf_plist.PlistBoolDictEntry(
                  name=otherjob, value=otherjobs[otherjob]))
      kwargs["KeepAliveDict"] = rdf_plist.LaunchdKeepAlive(**keepalivedict)

    envvars = plist.get("EnvironmentVariables")
    if envvars is not None:
      kwargs["EnvironmentVariables"] = []
      for envvar in envvars:
        kwargs["EnvironmentVariables"].append(
            rdf_plist.PlistStringDictEntry(name=envvar, value=envvars[envvar]))

    startcalendarinterval = plist.get("StartCalendarInterval")
    if startcalendarinterval is not None:
      if isinstance(startcalendarinterval, dict):
        kwargs["StartCalendarInterval"] = [
            rdf_plist.LaunchdStartCalendarIntervalEntry(
                Minute=startcalendarinterval.get("Minute"),
                Hour=startcalendarinterval.get("Hour"),
                Day=startcalendarinterval.get("Day"),
                Weekday=startcalendarinterval.get("Weekday"),
                Month=startcalendarinterval.get("Month"))
        ]
      else:
        kwargs["StartCalendarInterval"] = []
        for entry in startcalendarinterval:
          kwargs["StartCalendarInterval"].append(
              rdf_plist.LaunchdStartCalendarIntervalEntry(
                  Minute=entry.get("Minute"),
                  Hour=entry.get("Hour"),
                  Day=entry.get("Day"),
                  Weekday=entry.get("Weekday"),
                  Month=entry.get("Month")))

    yield rdf_plist.LaunchdPlist(**kwargs)
コード例 #19
0
    def Parse(self, statentry, file_object, knowledge_base):
        """Parse the Plist file."""
        _ = knowledge_base
        kwargs = {}
        try:
            kwargs["aff4path"] = file_object.urn
        except AttributeError:
            pass

        direct_copy_items = [
            "Label", "Disabled", "UserName", "GroupName", "Program",
            "StandardInPath", "StandardOutPath", "StandardErrorPath",
            "LimitLoadToSessionType", "EnableGlobbing", "EnableTransactions",
            "OnDemand", "RunAtLoad", "RootDirectory", "WorkingDirectory",
            "Umask", "TimeOut", "ExitTimeOut", "ThrottleInterval",
            "InitGroups", "StartOnMount", "StartInterval", "Debug",
            "WaitForDebugger", "Nice", "ProcessType", "AbandonProcessGroup",
            "LowPriorityIO", "LaunchOnlyOnce"
        ]

        string_array_items = [
            "LimitLoadToHosts", "LimitLoadFromHosts", "LimitLoadToSessionType",
            "ProgramArguments", "WatchPaths", "QueueDirectories"
        ]

        flag_only_items = [
            "SoftResourceLimits", "HardResourceLimits", "Sockets"
        ]

        plist = {}

        try:
            plist = binplist.readPlist(file_object)
        except (binplist.FormatError, ValueError, IOError) as e:
            plist["Label"] = "Could not parse plist: %s" % e

        # These are items that can be directly copied
        for key in direct_copy_items:
            kwargs[key] = plist.get(key)

        # These could be a string, they could be an array, we don't know and neither
        # does Apple so we check.
        for key in string_array_items:
            elements = plist.get(key)
            if isinstance(elements, basestring):
                kwargs[key] = [elements]
            else:
                kwargs[key] = elements

        # These are complex items that can appear in multiple data structures
        # so we only flag on their existence
        for key in flag_only_items:
            if plist.get(key):
                kwargs[key] = True

        if plist.get("inetdCompatability") is not None:
            kwargs["inetdCompatabilityWait"] = plist.get(
                "inetdCompatability").get("Wait")

        keepalive = plist.get("KeepAlive")
        if isinstance(keepalive, bool) or keepalive is None:
            kwargs["KeepAlive"] = keepalive
        else:
            keepalivedict = {}
            keepalivedict["SuccessfulExit"] = keepalive.get("SuccessfulExit")
            keepalivedict["NetworkState"] = keepalive.get("NetworkState")

            pathstates = keepalive.get("PathState")
            if pathstates is not None:
                keepalivedict["PathState"] = []
                for pathstate in pathstates:
                    keepalivedict["PathState"].append(
                        rdf_plist.PlistBoolDictEntry(
                            name=pathstate, value=pathstates[pathstate]))

            otherjobs = keepalive.get("OtherJobEnabled")
            if otherjobs is not None:
                keepalivedict["OtherJobEnabled"] = []
                for otherjob in otherjobs:
                    keepalivedict["OtherJobEnabled"].append(
                        rdf_plist.PlistBoolDictEntry(
                            name=otherjob, value=otherjobs[otherjob]))
            kwargs["KeepAliveDict"] = rdf_plist.LaunchdKeepAlive(
                **keepalivedict)

        envvars = plist.get("EnvironmentVariables")
        if envvars is not None:
            kwargs["EnvironmentVariables"] = []
            for envvar in envvars:
                kwargs["EnvironmentVariables"].append(
                    rdf_plist.PlistStringDictEntry(name=envvar,
                                                   value=envvars[envvar]))

        startcalendarinterval = plist.get("StartCalendarInterval")
        if startcalendarinterval is not None:
            if isinstance(startcalendarinterval, dict):
                kwargs["StartCalendarInterval"] = [
                    rdf_plist.LaunchdStartCalendarIntervalEntry(
                        Minute=startcalendarinterval.get("Minute"),
                        Hour=startcalendarinterval.get("Hour"),
                        Day=startcalendarinterval.get("Day"),
                        Weekday=startcalendarinterval.get("Weekday"),
                        Month=startcalendarinterval.get("Month"))
                ]
            else:
                kwargs["StartCalendarInterval"] = []
                for entry in startcalendarinterval:
                    kwargs["StartCalendarInterval"].append(
                        rdf_plist.LaunchdStartCalendarIntervalEntry(
                            Minute=entry.get("Minute"),
                            Hour=entry.get("Hour"),
                            Day=entry.get("Day"),
                            Weekday=entry.get("Weekday"),
                            Month=entry.get("Month")))

        yield rdf_plist.LaunchdPlist(**kwargs)