Module: Puppet::Util::Plist

Defined in:
lib/puppet/util/plist.rb

Defined Under Namespace

Classes: FormatError

Class Method Summary collapse

Class Method Details

.binary_plist_magic_numberObject

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Defines the magic number for binary plists


15
16
17
# File 'lib/puppet/util/plist.rb', line 15

def binary_plist_magic_number
  "bplist00"
end

.convert_cfpropertylist_to_native_types(plist_obj) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Helper method to assist in converting a native CFPropertyList object to a native Ruby object (hash). It's its own method for stubbing purposes


111
112
113
# File 'lib/puppet/util/plist.rb', line 111

def convert_cfpropertylist_to_native_types(plist_obj)
  CFPropertyList.native_types(plist_obj.value)
end

.dump_plist(plist_data, format = :xml) ⇒ Object


155
156
157
158
159
# File 'lib/puppet/util/plist.rb', line 155

def dump_plist(plist_data, format = :xml)
  plist_to_save       = CFPropertyList::List.new
  plist_to_save.value = CFPropertyList.guess(plist_data)
  plist_to_save.to_str(to_format(format), :formatted => true)
end

.new_cfpropertylist(plist_opts) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Helper method to assist in generating a new CFPropertyList Plist. It's its own method for stubbing purposes


103
104
105
# File 'lib/puppet/util/plist.rb', line 103

def new_cfpropertylist(plist_opts)
  CFPropertyList::List.new(plist_opts)
end

.open_file_with_args(file, args) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Helper method to assist in reading a file. It's its own method for stubbing purposes

Parameters:

  • args (String)

    Extra file operation mode information to use (defaults to read-only mode 'r') This is the standard mechanism Ruby uses in the IO class, and therefore encoding may be explicitly like fmode : encoding or fmode : “BOM|UTF-*” for example, a:ASCII or w+:UTF-8


95
96
97
# File 'lib/puppet/util/plist.rb', line 95

def open_file_with_args(file, args)
  File.open(file, args).read
end

.parse_plist(plist_data, file_path = '') ⇒ Object

Read plist text using the CFPropertyList gem.


57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
# File 'lib/puppet/util/plist.rb', line 57

def parse_plist(plist_data, file_path = '')
  bad_xml_doctype = /^.*<!DOCTYPE plist PUBLIC -\/\/Apple Computer.*$/
  # Depending on where parse_plist is called from, plist_data can be either XML or binary.
  # If we get XML, make sure ruby knows it's UTF-8 so we avoid invalid byte sequence errors.
  if plist_data.include?('encoding="UTF-8"') && plist_data.encoding != Encoding::UTF_8
    plist_data.force_encoding(Encoding::UTF_8)
  end

  begin
    if plist_data =~ bad_xml_doctype
      plist_data.gsub!( bad_xml_doctype, plist_xml_doctype )
      Puppet.debug("Had to fix plist with incorrect DOCTYPE declaration: #{file_path}")
    end
  rescue ArgumentError => e
    Puppet.debug "Failed with #{e.class} on #{file_path}: #{e.inspect}"
    return nil
  end

  begin
    plist_obj = new_cfpropertylist(:data => plist_data)
  # CFPropertyList library will raise NoMethodError for invalid data
  rescue CFFormatError, NoMethodError => e
    Puppet.debug "Failed with #{e.class} on #{file_path}: #{e.inspect}"
    return nil
  end
  convert_cfpropertylist_to_native_types(plist_obj)
end

.plist_xml_doctypeObject

Defines a default doctype string that should be at the top of most plist files. Useful if we need to modify an invalid doctype string in memory. In version 10.9 and lower of OS X the plist at /System/Library/LaunchDaemons/org.ntp.ntpd.plist had an invalid doctype string. This corrects for that.


24
25
26
# File 'lib/puppet/util/plist.rb', line 24

def plist_xml_doctype
  '<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">'
end

.read_file_with_offset(file_path, offset) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Helper method to assist in reading a file with an offset value. It's its own method for stubbing purposes


127
128
129
# File 'lib/puppet/util/plist.rb', line 127

def read_file_with_offset(file_path, offset)
  IO.read(file_path, offset)
end

.read_plist_file(file_path) ⇒ Object

Read a plist file, whether its format is XML or in Apple's “binary1” format, using the CFPropertyList gem.


30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
# File 'lib/puppet/util/plist.rb', line 30

def read_plist_file(file_path)
  # We can't really read the file until we know the source encoding in
  # Ruby 1.9.x, so we use the magic number to detect it.
  # NOTE: We used IO.read originally to be Ruby 1.8.x compatible.
  if read_file_with_offset(file_path, binary_plist_magic_number.length) == binary_plist_magic_number
    plist_obj = new_cfpropertylist(:file => file_path)
    return convert_cfpropertylist_to_native_types(plist_obj)
  else
    plist_data = open_file_with_args(file_path, "r:UTF-8")
    plist = parse_plist(plist_data, file_path)
    return plist if plist

    Puppet.debug "Plist #{file_path} ill-formatted, converting with plutil"
    begin
      plist = Puppet::Util::Execution.execute(['/usr/bin/plutil', '-convert', 'xml1', '-o', '-', file_path],
                                              {:failonfail => true, :combine => true})
      return parse_plist(plist)
    rescue Puppet::ExecutionFailure => detail
      message = _("Cannot read file %{file_path}; Puppet is skipping it.") % { file_path: file_path }
      message += '\n' + _("Details: %{detail}") % { detail: detail }
      Puppet.warning(message)
    end
  end
  return nil
end

.string_to_blob(str) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Helper method to convert a string into a CFProperty::Blob, which is needed to properly handle binary strings


119
120
121
# File 'lib/puppet/util/plist.rb', line 119

def string_to_blob(str)
  CFPropertyList::Blob.new(str)
end

.to_format(format) ⇒ Object


131
132
133
134
135
136
137
138
139
140
141
# File 'lib/puppet/util/plist.rb', line 131

def to_format(format)
  if format.to_sym == :xml
    CFPropertyList::List::FORMAT_XML
  elsif format.to_sym == :binary
    CFPropertyList::List::FORMAT_BINARY
  elsif format.to_sym == :plain
    CFPropertyList::List::FORMAT_PLAIN
  else
    raise FormatError.new "Unknown plist format #{format}"
  end
end

.write_plist_file(plist, file_path, format = :xml) ⇒ Object

This method will write a plist file using a specified format (or XML by default)


145
146
147
148
149
150
151
152
153
# File 'lib/puppet/util/plist.rb', line 145

def write_plist_file(plist, file_path, format = :xml)
  begin
    plist_to_save       = CFPropertyList::List.new
    plist_to_save.value = CFPropertyList.guess(plist)
    plist_to_save.save(file_path, to_format(format), :formatted => true)
  rescue IOError => e
    Puppet.err(_("Unable to write the file %{file_path}. %{error}") % { file_path: file_path, error: e.inspect })
  end
end