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, BlockParser::USE_MAIN_DATA

Instance Attribute Summary collapse

Attributes inherited from BlockParser

#data_chunks, #node

Instance Method Summary collapse

Methods inherited from BlockParser

#get_data_array, #get_data_indirect, #get_data_indirect_io, #handle_indirect_values, #load_page_header, #load_root_header

Constructor Details

#initialize(node, local_node_id) ⇒ RawPropertyStoreTable

Returns a new instance of RawPropertyStoreTable.

Parameters:

  • node (NodePtr)
  • local_node_id (Integer)

Raises:



1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
# File 'lib/mapi/pst.rb', line 1394

def initialize node, local_node_id
	super
	bTypeTC = 0x7c
	raise FormatError, "expected type 124 - got #{@heap_type}" unless @heap_type == bTypeTC

	header_data = get_data_indirect @offset1
	# seven_c_blk
	# often: u1 == u2 and u3 == u2 + 2, then rec_size == u3 + 4. wtf
	# TCINFO
	seven_c, @num_list, u1, u2, u3, @rec_size, b_five_offset,
		rows_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 = node.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.
	if rows_offset != 0
		#if RangesIOIdxChain === @rows_io
		#	@data3_idxs = 
		#	# modify ranges
		#	ranges = @rows_io.ranges.map { |offset, size| [offset, size / @rec_size * @rec_size] }
		#	@rows_io.instance_variable_set :@ranges, ranges
		#end
		@rows_pages = get_data_array(rows_offset)
	else
		# table rows are empty, no data to be read
		@rows_pages = [""]
	end

	# 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:
	@rows_per_page = @rows_pages.first.length / @rec_size

	@length = @rows_pages.map { |data| data.length / @rec_size }.sum

	#end

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

Instance Attribute Details

#data2String (readonly)

Returns 2.3.2 BTree-on-Heap (BTH).

Returns:

  • (String)

    2.3.2 BTree-on-Heap (BTH)



1384
1385
1386
# File 'lib/mapi/pst.rb', line 1384

def data2
  @data2
end

#index_dataString (readonly)

Returns Array of TCOLDESC.

Returns:

  • (String)

    Array of TCOLDESC



1382
1383
1384
# File 'lib/mapi/pst.rb', line 1382

def index_data
  @index_data
end

#lengthInteger (readonly)

Returns record count.

Returns:

  • (Integer)

    record count



1380
1381
1382
# File 'lib/mapi/pst.rb', line 1380

def length
  @length
end

#rec_sizeInteger (readonly)

Returns TCI_bm.

Returns:

  • (Integer)

    TCI_bm



1388
1389
1390
# File 'lib/mapi/pst.rb', line 1388

def rec_size
  @rec_size
end

#rows_pagesArray<String> (readonly)

Returns 2.3.4.4 Row Matrix.

Returns:

  • (Array<String>)

    2.3.4.4 Row Matrix



1386
1387
1388
# File 'lib/mapi/pst.rb', line 1386

def rows_pages
  @rows_pages
end

#rows_per_pageInteger (readonly)

Returns:

  • (Integer)


1390
1391
1392
# File 'lib/mapi/pst.rb', line 1390

def rows_per_page
  @rows_per_page
end

Instance Method Details

#[](idx) ⇒ Row

return grid row

Parameters:

  • idx (Integer)

Returns:



1480
1481
1482
1483
# File 'lib/mapi/pst.rb', line 1480

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

#each {|row| ... } ⇒ Object

Yields:

  • (row)

Yield Parameters:



1487
1488
1489
# File 'lib/mapi/pst.rb', line 1487

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

#get_record(record_index) ⇒ String

get record data

Parameters:

  • record_index (Integer)

Returns:

  • (String)


1496
1497
1498
1499
1500
# File 'lib/mapi/pst.rb', line 1496

def get_record record_index
	page_index = record_index / @rows_per_page
	heap_index = record_index % @rows_per_page
	(@rows_pages[page_index])[@rec_size * heap_index, @rec_size]
end

#schemaArray<Column>

for debug

Returns:



1472
1473
1474
# File 'lib/mapi/pst.rb', line 1472

def schema
	@schema ||= Pst.split_per(index_data, 8, -1).map { |data| Column.new data }
end