# -*- 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 Licenserequire'base64'moduleCFPropertyList### Blob is intended to distinguish between a Ruby String instance that should# be converted to a CFString type and a Ruby String instance that should be# converted to a CFData typeclassBlob<Stringend### UidFixnum is intended to distinguish between a Ruby Integer# instance that should be converted to a CFInteger/CFReal type and a# Ruby Integer instance that should be converted to a CFUid type.classUidFixnum<Integerend# This class defines the base class for all CFType classes#classCFType# value of the typeattr_accessor:valuedefinitialize(value=nil)@value=valueenddefto_xml(parser)enddefto_binary(bplist)enddefto_plain(plist)endend# 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)classCFString<CFType# convert to XMLdefto_xml(parser)n=parser.new_node('string')n=parser.append_node(n,parser.new_text(@value))unless@value.nil?nend# convert to binarydefto_binary(bplist)bplist.string_to_binary(@value);enddefto_plain(plist)if@value=~/^\w+$/@valueelsequotedendenddefquotedstr='"'@value.each_chardo|c|str<<casecwhen'"''\\"'when'\\''\\'when"\a""\\a"when"\b""\\b"when"\f""\\f"when"\n""\n"when"\v""\\v"when"\r""\\r"when"\t""\\t"elsecendendstr<<'"'endend# This class holds integer/fixnum valuesclassCFInteger<CFType# convert to XMLdefto_xml(parser)n=parser.new_node('integer')n=parser.append_node(n,parser.new_text(@value.to_s))nend# convert to binarydefto_binary(bplist)bplist.num_to_binary(self)enddefto_plain(plist)@value.to_sendend# This class holds float valuesclassCFReal<CFType# convert to XMLdefto_xml(parser)n=parser.new_node('real')n=parser.append_node(n,parser.new_text(@value.to_s))nend# convert to binarydefto_binary(bplist)bplist.num_to_binary(self)enddefto_plain(plist)@value.to_sendend# 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 timestampclassCFDate<CFTypeTIMESTAMP_APPLE=0TIMESTAMP_UNIX=1DATE_DIFF_APPLE_UNIX=978307200# create a XML date strimg from a time objectdefCFDate.date_string(val)# 2009-05-13T20:23:43Zval.getutc.strftime("%Y-%m-%dT%H:%M:%SZ")end# parse a XML date stringdefCFDate.parse_date(val)# 2009-05-13T20:23:43Zval=~%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,$6returnTime.utc(year,month,day,hour,min,sec).getlocalend# set value to defined statedefinitialize(value=nil,format=CFDate::TIMESTAMP_UNIX)if(value.is_a?(Time)||value.nil?)then@value=value.nil??Time.now:valueelsifvalue.instance_of?Date@value=Time.utc(value.year,value.month,value.day,0,0,0)elsifvalue.instance_of?DateTime@value=value.to_time.utcelseset_value(value,format)endend# set value with timestamp, either Apple or UNIXdefset_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)endend# get timestamp, either UNIX or Apple timestampdefget_value(format=CFDate::TIMESTAMP_UNIX)if(format==CFDate::TIMESTAMP_UNIX)then@value.to_ielse@value.to_f-CFDate::DATE_DIFF_APPLE_UNIXendend# convert to XMLdefto_xml(parser)n=parser.new_node('date')n=parser.append_node(n,parser.new_text(CFDate::date_string(@value)))nend# convert to binarydefto_binary(bplist)bplist.date_to_binary(@value)enddefto_plain(plist)@value.strftime("%Y-%m-%d %H:%M:%S %z")endend# This class contains a boolean valueclassCFBoolean<CFType# convert to XMLdefto_xml(parser)parser.new_node(@value?'true':'false')end# convert to binarydefto_binary(bplist)bplist.bool_to_binary(@value);enddefto_plain(plist)@value?"true":"false"endend# This class contains binary data valuesclassCFData<CFType# Base64 encoded dataDATA_BASE64=0# Raw dataDATA_RAW=1# set value to defined state, either base64 encoded or rawdefinitialize(value=nil,format=DATA_BASE64)if(format==DATA_RAW)@raw_value=valueelse@value=valueendend# get base64 encoded valuedefencoded_value@value||="\n#{Base64.encode64(@raw_value).gsub("\n",'').scan(/.{1,76}/).join("\n")}\n"end# get base64 decoded valuedefdecoded_value@raw_value||=Blob.new(Base64.decode64(@value))end# convert to XMLdefto_xml(parser)n=parser.new_node('data')n=parser.append_node(n,parser.new_text(encoded_value()))nend# convert to binarydefto_binary(bplist)bplist.data_to_binary(decoded_value())enddefto_plain(plist)"<"+decoded_value.unpack("H*").join("")+">"endend# This class contains an array of valuesclassCFArray<CFType# create a new array CFTypedefinitialize(val=[])@value=valend# convert to XMLdefto_xml(parser)n=parser.new_node('array')@value.eachdo|v|n=parser.append_node(n,v.to_xml(parser))endnend# convert to binarydefto_binary(bplist)bplist.array_to_binary(self)enddefto_plain(plist)ary=@value.map{|v|v.to_plain(plist)}"( "+ary.join(", ")+" )"endend# this class contains a hash of valuesclassCFDictionary<CFType# Create new CFDictonary type.definitialize(value={})@value=valueend# convert to XMLdefto_xml(parser)n=parser.new_node('dict')@value.each_pairdo|key,value|k=parser.append_node(parser.new_node('key'),parser.new_text(key.to_s))n=parser.append_node(n,k)n=parser.append_node(n,value.to_xml(parser))endnend# convert to binarydefto_binary(bplist)bplist.dict_to_binary(self)enddefto_plain(plist)str="{ "cfstr=CFString.new()@value.eachdo|k,v|cfstr.value=kstr<<cfstr.to_plain(plist)+" = "+v.to_plain(plist)+"; "endstr<<"}"endendclassCFUid<CFTypedefto_xml(parser)CFDictionary.new({'CF$UID'=>CFInteger.new(@value)}).to_xml(parser)end# convert to binarydefto_binary(bplist)bplist.uid_to_binary(@value)enddefto_plain(plist)CFDictionary.new({'CF$UID'=>CFInteger.new(@value)}).to_plain(plist)endendend# eof