# -*- coding: utf-8 -*- # # CFTypes, e.g. CFString, CFInteger # needed to create unambiguous plists # # Author:: Christian Kruse (mailto:cjk@wwwtech.de) # Copyright:: Copyright (c) 2009 # License:: MIT License require 'base64' module CFPropertyList # This class defines the base class for all CFType classes # class CFType # value of the type attr_accessor :value def initialize(value=nil) @value = value end def to_xml; end def to_binary(bplist) end end # This class holds string values, both, UTF-8 and UTF-16BE # It will convert the value to UTF-16BE if necessary (i.e. if non-ascii char contained) class CFString < CFType # convert to XML def to_xml n = LibXML::XML::Node.new('string') n << LibXML::XML::Node.new_text(@value) unless @value.nil? n end # convert to binary def to_binary(bplist) bplist.string_to_binary(@value); end end # This class holds integer/fixnum values class CFInteger < CFType # convert to XML def to_xml return LibXML::XML::Node.new('integer') << LibXML::XML::Node.new_text(@value.to_s) end # convert to binary def to_binary(bplist) bplist.num_to_binary(self) end end # This class holds float values class CFReal < CFType # convert to XML def to_xml return LibXML::XML::Node.new('real') << LibXML::XML::Node.new_text(@value.to_s) end # convert to binary def to_binary(bplist) bplist.num_to_binary(self) end end # This class holds Time values. While Apple uses seconds since 2001, # the rest of the world uses seconds since 1970. So if you access value # directly, you get the Time class. If you access via get_value you either # geht the timestamp or the Apple timestamp class CFDate < CFType TIMESTAMP_APPLE = 0 TIMESTAMP_UNIX = 1; DATE_DIFF_APPLE_UNIX = 978307200 # create a XML date strimg from a time object def CFDate.date_string(val) # 2009-05-13T20:23:43Z val.getutc.strftime("%Y-%m-%dT%H:%M:%SZ") end # parse a XML date string def CFDate.parse_date(val) # 2009-05-13T20:23:43Z val =~ %r{^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})Z$} year,month,day,hour,min,sec = $1, $2, $3, $4, $5, $6 return Time.utc(year,month,day,hour,min,sec).getlocal end # set value to defined state def initialize(value = nil,format=CFDate::TIMESTAMP_UNIX) if(value.is_a?(Time) || value.nil?) then @value = value.nil? ? Time.now : value elsif value.instance_of? Date @value = Time.utc(value.year, value.month, value.day, 0, 0, 0) elsif value.instance_of? DateTime @value = value.to_time.utc else set_value(value,format) end end # set value with timestamp, either Apple or UNIX def set_value(value,format=CFDate::TIMESTAMP_UNIX) if(format == CFDate::TIMESTAMP_UNIX) then @value = Time.at(value) else @value = Time.at(value + CFDate::DATE_DIFF_APPLE_UNIX) end end # get timestamp, either UNIX or Apple timestamp def get_value(format=CFDate::TIMESTAMP_UNIX) if(format == CFDate::TIMESTAMP_UNIX) then @value.to_i else @value.to_f - CFDate::DATE_DIFF_APPLE_UNIX end end # convert to XML def to_xml LibXML::XML::Node.new('date') << LibXML::XML::Node.new_text(CFDate::date_string(@value)) end # convert to binary def to_binary(bplist) bplist.date_to_binary(@value) end end # This class contains a boolean value class CFBoolean < CFType # convert to XML def to_xml LibXML::XML::Node.new(@value ? 'true' : 'false') end # convert to binary def to_binary(bplist) bplist.bool_to_binary(@value); end end # This class contains binary data values class CFData < CFType # Base64 encoded data DATA_BASE64 = 0 # Raw data DATA_RAW = 1 # set value to defined state, either base64 encoded or raw def initialize(value=nil,format=DATA_BASE64) if(format == DATA_RAW) @raw_value = value @raw_value.blob = true else @value = value end end # get base64 encoded value def encoded_value @value ||= "\n#{Base64.encode64(@raw_value).gsub("\n", '').scan(/.{1,76}/).join("\n")}\n" end # get base64 decoded value def decoded_value @raw_value ||= String.new(Base64.decode64(@value)) @raw_value.blob = true @raw_value end # convert to XML def to_xml LibXML::XML::Node.new('data') << LibXML::XML::Node.new_text(encoded_value()) end # convert to binary def to_binary(bplist) bplist.data_to_binary(decoded_value()) end end # This class contains an array of values class CFArray < CFType # create a new array CFType def initialize(val=[]) @value = val end # convert to XML def to_xml n = LibXML::XML::Node.new('array') @value.each do |v| n << v.to_xml end n end # convert to binary def to_binary(bplist) bplist.array_to_binary(self) end end =begin class CFEnumerator < CFArray def to_binary(bplist) bplist.enum_to_binary(self) end end =end # this class contains a hash of values class CFDictionary < CFType # Create new CFDictonary type. def initialize(value={}) @value = value end # convert to XML def to_xml n = LibXML::XML::Node.new('dict') @value.each_pair do |key,value| k = LibXML::XML::Node.new('key') << LibXML::XML::Node.new_text(key) n << k n << value.to_xml end n end # convert to binary def to_binary(bplist) bplist.dict_to_binary(self) end end end # eof