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



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

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



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

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

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



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

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



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

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



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

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.



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
84
# File 'lib/puppet/util/plist.rb', line 58

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.



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

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



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

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.



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

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



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

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

.to_format(format) ⇒ Object



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

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)



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

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