Class: Webloc

Inherits:
Object
  • Object
show all
Defined in:
lib/webloc.rb,
lib/webloc/version.rb

Defined Under Namespace

Classes: CorruptedFileError, EmptyFileError, FileNotFoundError, InvalidFormatError, WeblocError

Constant Summary collapse

VERSION =
"0.3.1"

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(url) ⇒ Webloc

Returns a new instance of Webloc.

Raises:

  • (ArgumentError)


12
13
14
15
# File 'lib/webloc.rb', line 12

def initialize(url)
  raise ArgumentError, "URL cannot be nil or empty" if url.nil? || url.empty?
  @url = url
end

Instance Attribute Details

#urlObject

Returns the value of attribute url.



10
11
12
# File 'lib/webloc.rb', line 10

def url
  @url
end

Class Method Details

.load(filename) ⇒ Object

Raises:



17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
# File 'lib/webloc.rb', line 17

def self.load(filename)
  raise FileNotFoundError, "File not found: #{filename}" unless File.exist?(filename)
  
  begin
    data = File.read(filename)
  rescue => e
    raise FileNotFoundError, "Unable to read file '#{filename}': #{e.message}"
  end
  
  raise EmptyFileError, "File is empty: #{filename}" if data.empty?
  
  data = data.force_encoding('binary') rescue data
  url = nil
  
  if data !~ /\<plist/
    # Handle binary plist format
    url = parse_binary_format(data, filename)
  else
    # Handle XML plist format
    url = parse_xml_format(filename)
  end
  
  raise CorruptedFileError, "No URL found in webloc file: #{filename}" if url.nil? || url.empty?
  new(url)
end

Instance Method Details

#dataObject



95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
# File 'lib/webloc.rb', line 95

def data
  # PLIST HEADER
  @data = "bplist\x30\x30".bytes

  # PLIST OBJECT TABLE
  @data += "\xD1\x01\x02".bytes   # object 1 is a dictionary
  @data += "SURL".bytes           # object 2

  length_suffix = @url.length > 255 ? "\x11" : "\x10"
  @data += ("\x5f" + length_suffix).bytes       # object 3 is an ASCII string with a variable length length encoding (I know..)
                            #   .. the '0' in \x10 denotes the length can be encoded within 2**0 bytes (i.e. 1)
                            #   .. the '1' in \x11 denotes the length can be encoded within 2**1 bytes (i.e. 2)
  
  if @url.length > 255
    @data += [@url.length].pack('S>').bytes
  else
    @data += [@url.length].pack('C').bytes
  end
  @data += @url.bytes             # and finally the URL itself

  # This is the offset table
  @data += "\x08\x0B\x0F".bytes   # so objects at 0x08, 0x0b and 0x0f

  # PLIST TRAILER
  # Bytes 0-4 are unused
  @data += "\x00\x00\x00\x00\x00".bytes
  # Byte 5 is the sort version
  @data += "\x00".bytes
  # Byte 6 is how many bytes are needed for each offset table offset
  @data += "\x01".bytes
  @data += "\x01".bytes
  # Bytes 8-15 are how many objects are contained in the plist
  @data += "\x00\x00\x00\x00\x00\x00\x00\x03".bytes
  # Bytes 16-23 are for an offset from the offset table
  @data += "\x00\x00\x00\x00\x00\x00\x00\x00".bytes
  # Bytes 24-31 denote the position of the offset table from the start of the file
  @data += "\x00\x00\x00\x00\x00\x00".bytes + [@url.length + 18].pack('S>').bytes

  @data = @data.pack('C*')
end

#save(filename) ⇒ Object

Raises:

  • (ArgumentError)


136
137
138
139
140
141
142
143
144
# File 'lib/webloc.rb', line 136

def save(filename)
  raise ArgumentError, "Filename cannot be nil or empty" if filename.nil? || filename.empty?
  
  begin
    File.open(filename, 'wb') { |f| f.write data }
  rescue => e
    raise WeblocError, "Failed to save webloc file '#{filename}': #{e.message}"
  end
end