#!/usr/bin/env python # -*- coding: windows-1251 -*- # Copyright (C) 2005 Roman V. Kiseliov # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in # the documentation and/or other materials provided with the # distribution. # # 3. All advertising materials mentioning features or use of this # software must display the following acknowledgment: # "This product includes software developed by # Roman V. Kiseliov ." # # 4. Redistributions of any form whatsoever must retain the following # acknowledgment: # "This product includes software developed by # Roman V. Kiseliov ." # # THIS SOFTWARE IS PROVIDED BY Roman V. Kiseliov ``AS IS'' AND ANY # EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Roman V. Kiseliov OR # ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT # NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, # STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED # OF THE POSSIBILITY OF SUCH DAMAGE. __rev_id__ = """$Id: BIFFRecords.py,v 1.7 2005/10/26 07:44:24 rvk Exp $""" from struct import pack from UnicodeUtils import * import sys class SharedStringTable(object): _SST_ID = 0x00FC _CONTINUE_ID = 0x003C def __init__(self): self._sst_record = '' self._continues = [] self._current_piece = pack(' 0xFFFF: raise Exception('error: very long string.') is_unicode_str = u_str[2] == '\x01' if is_unicode_str: atom_len = 5 # 2 byte -- len, # 1 byte -- options, # 2 byte -- 1st sym else: atom_len = 4 # 2 byte -- len, # 1 byte -- options, # 1 byte -- 1st sym self._save_atom(u_str[0:atom_len]) self._save_splitted(u_str[atom_len:], is_unicode_str) def _new_piece(self): if self._sst_record == '': self._sst_record = self._current_piece else: curr_piece_len = len(self._current_piece) self._continues.append(pack('<2H%ds'%curr_piece_len, self._CONTINUE_ID, curr_piece_len, self._current_piece)) self._current_piece = '' self._pos = len(self._current_piece) def _save_atom(self, s): atom_len = len(s) free_space = 0x2020 - len(self._current_piece) if free_space < atom_len: self._new_piece() self._current_piece += s def _save_splitted(self, s, is_unicode_str): i = 0 str_len = len(s) while i < str_len: piece_len = len(self._current_piece) free_space = 0x2020 - piece_len tail_len = str_len - i need_more_space = free_space < tail_len if not need_more_space: atom_len = tail_len else: if is_unicode_str: atom_len = free_space & 0xFFFE else: atom_len = free_space self._current_piece += s[i:i+atom_len] if need_more_space: self._new_piece() if is_unicode_str: self._current_piece += '\x01' else: self._current_piece += '\x00' i += atom_len class BiffRecord(object): def __init__(self): self._rec_data = '' def get_rec_id(self): return _REC_ID def get_rec_header(self): return pack('<2H', self._REC_ID, len(self._rec_data)) def get_rec_data(self): return self._rec_data def get(self): data = self.get_rec_data() if len(data) > 0x2020: # limit for BIFF7/8 chunks = [] pos = 0 while pos < len(data): chunk_pos = pos + 0x2020 chunk = data[pos:chunk_pos] chunks.append(chunk) pos = chunk_pos continues = pack('<2H', self._REC_ID, len(chunks[0])) + chunks[0] for chunk in chunks[1:]: continues += pack('<2H%ds'%len(chunk), 0x003C, len(chunk), chunk) # 0x003C -- CONTINUE record id return continues else: return self.get_rec_header() + data class Biff8BOFRecord(BiffRecord): """ Offset Size Contents 0 2 Version, contains 0600H for BIFF8 and BIFF8X 2 2 Type of the following data: 0005H = Workbook globals 0006H = Visual Basic module 0010H = Worksheet 0020H = Chart 0040H = Macro sheet 0100H = Workspace file 4 2 Build identifier 6 2 Build year 8 4 File history flags 12 4 Lowest Excel version that can read all records in this file """ _REC_ID = 0x0809 # stream types BOOK_GLOBAL = 0x0005 VB_MODULE = 0x0006 WORKSHEET = 0x0010 CHART = 0x0020 MACROSHEET = 0x0040 WORKSPACE = 0x0100 def __init__(self, rec_type): BiffRecord.__init__(self) version = 0x0600 build = 0x0DBB year = 0x07CC file_hist_flags = 0x00L ver_can_read = 0x06L self._rec_data = pack('<4H2I', version, rec_type, build, year, file_hist_flags, ver_can_read) class InteraceHdrRecord(BiffRecord): _REC_ID = 0x00E1 def __init__(self): BiffRecord.__init__(self) self._rec_data = pack('BB', 0xB0, 0x04) class InteraceEndRecord(BiffRecord): _REC_ID = 0x00E2 def __init__(self): BiffRecord.__init__(self) self._rec_data = '' class MMSRecord(BiffRecord): _REC_ID = 0x00C1 def __init__(self): BiffRecord.__init__(self) self._rec_data = pack('> 15 c = low_15 | high_15 passwd_hash ^= c passwd_hash ^= len(plaintext) passwd_hash ^= 0xCE4B return passwd_hash def __init__(self, passwd = ""): BiffRecord.__init__(self) self._rec_data = pack('