Class: Mapi::Pst::RawPropertyStoreTable

Inherits:
BlockParser show all
Includes:
Enumerable
Defined in:
lib/mapi/pst.rb

Overview

RawPropertyStoreTable is kind of like a database table. it has a fixed set of columns. #[] is kind of like getting a row from the table. those rows are currently encapsulated by Row, which has #each like RawPropertyStore. only used for the recipients array, and the attachments array. completely lazy, doesn’t load any of the properties upon creation.

Defined Under Namespace

Classes: Column, Row

Constant Summary

Constants inherited from BlockParser

BlockParser::ID2_ATTACHMENTS, BlockParser::ID2_RECIPIENTS, BlockParser::IMMEDIATE_TYPES, BlockParser::INDIRECT_TYPES, BlockParser::PR_BODY_HTML, BlockParser::PR_SUBJECT, BlockParser::TYPES

Instance Attribute Summary collapse

Attributes inherited from BlockParser

#data, #data_chunks, #desc, #offset_tables

Instance Method Summary collapse

Methods inherited from BlockParser

#get_data_indirect, #get_data_indirect_io, #handle_indirect_values, #idx2, #load_header

Constructor Details

#initialize(desc) ⇒ RawPropertyStoreTable

Returns a new instance of RawPropertyStoreTable.

Raises:



1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
# File 'lib/mapi/pst.rb', line 1332

def initialize desc
	super
	raise FormatError, "expected type 2 - got #{@type}" unless @type == 2

	header_data = get_data_indirect @offset1
	# seven_c_blk
	# often: u1 == u2 and u3 == u2 + 2, then rec_size == u3 + 4. wtf
	seven_c, @num_list, u1, u2, u3, @rec_size, b_five_offset,
		ind2_offset, u7, u8 = header_data[0, 22].unpack('CCv4V2v2')
	@index_data = header_data[22..-1]

	raise FormatError if @num_list != schema.length or seven_c != 0x7c
	# another check
	min_size = schema.inject(0) { |total, col| total + col.size }
	# seem to have at max, 8 padding bytes on the end of the record. not sure if it means
	# anything. maybe its just space that hasn't been reclaimed due to columns being
	# removed or something. probably should just check lower bound. 
	range = (min_size..min_size + 8)
	warn "rec_size seems wrong (#{range} !=== #{rec_size})" unless range === rec_size

	header_data2 = get_data_indirect b_five_offset
	raise FormatError if header_data2.length < 8
	signature, offset2 = header_data2.unpack 'V2'
	# ??? seems a bit iffy
	# there's probably more to the differences than this, and the data2 difference below
	expect = desc.pst.header.version_2003? ? 0x000404b5 : 0x000204b5
	raise FormatError, 'unhandled block signature 0x%08x' % signature if signature != expect

	# this holds all the row data
	# handle multiple block issue.
	@data3_io = get_data_indirect_io ind2_offset
	if RangesIOIdxChain === @data3_io
		@data3_idxs = 
		# modify ranges
		ranges = @data3_io.ranges.map { |offset, size| [offset, size / @rec_size * @rec_size] }
		@data3_io.instance_variable_set :@ranges, ranges
	end
	@data3 = @data3_io.read

	# there must be something to the data in data2. i think data2 is the array of objects essentially.
	# currently its only used to imply a length
	# actually, at size 6, its just some auxiliary data. i'm thinking either Vv/vV, for 97, and something
	# wider for 03. the second value is just the index (0...length), and the first value is
	# some kind of offset i expect. actually, they were all id2 values, in another case.
	# so maybe they're get_data_indirect values too?
	# actually, it turned out they were identical to the PR_ATTACHMENT_ID2 values...
	# id2_values = ie, data2.unpack('v*').to_enum(:each_slice, 3).transpose[0]
	# table[i].assoc(PR_ATTACHMENT_ID2).last == id2_values[i], for all i. 
	@data2 = get_data_indirect(offset2) rescue nil
	#if data2
	#	@length = (data2.length / 6.0).ceil
	#else
	# the above / 6, may have been ok for 97 files, but the new 0x0004 style block must have
	# different size records... just use this instead:
		# hmmm, actually, we can still figure it out:
		@length = @data3.length / @rec_size
	#end

	# lets try and at least use data2 for a warning for now
	if data2
		data2_rec_size = desc.pst.header.version_2003? ? 8 : 6
		warn 'somthing seems wrong with data3' unless @length == (data2.length / data2_rec_size)
	end
end

Instance Attribute Details

#data2Object (readonly)

Returns the value of attribute data2.



1331
1332
1333
# File 'lib/mapi/pst.rb', line 1331

def data2
  @data2
end

#data3Object (readonly)

Returns the value of attribute data3.



1331
1332
1333
# File 'lib/mapi/pst.rb', line 1331

def data3
  @data3
end

#index_dataObject (readonly)

Returns the value of attribute index_data.



1331
1332
1333
# File 'lib/mapi/pst.rb', line 1331

def index_data
  @index_data
end

#lengthObject (readonly)

Returns the value of attribute length.



1331
1332
1333
# File 'lib/mapi/pst.rb', line 1331

def length
  @length
end

#rec_sizeObject (readonly)

Returns the value of attribute rec_size.



1331
1332
1333
# File 'lib/mapi/pst.rb', line 1331

def rec_size
  @rec_size
end

Instance Method Details

#[](idx) ⇒ Object



1401
1402
1403
1404
# File 'lib/mapi/pst.rb', line 1401

def [] idx
	# handle funky rounding
	Row.new self, idx * @rec_size
end

#eachObject



1406
1407
1408
# File 'lib/mapi/pst.rb', line 1406

def each
	length.times { |i| yield self[i] }
end

#schemaObject



1397
1398
1399
# File 'lib/mapi/pst.rb', line 1397

def schema
	@schema ||= index_data.scan(/.{8}/m).map { |data| Column.new data }
end