filters

klaola.cc

00001 /* This file is part of the KDE project
00002    Copyright (C) 1999 Werner Trobin <trobin@kde.org>
00003 
00004    This library is free software; you can redistribute it and/or
00005    modify it under the terms of the GNU Library General Public
00006    License as published by the Free Software Foundation; either
00007    version 2 of the License, or (at your option) any later version.
00008 
00009    This library is distributed in the hope that it will be useful,
00010    but WITHOUT ANY WARRANTY; without even the implied warranty of
00011    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012    Library General Public License for more details.
00013 
00014    You should have received a copy of the GNU Library General Public License
00015    along with this library; see the file COPYING.LIB.  If not, write to
00016    the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
00017    Boston, MA 02111-1307, USA.
00018 */
00019 
00020 #include <klaola.h>
00021 #include <kdebug.h>
00022 
00023 const int KLaola::s_area = 30510;
00024 
00025 KLaola::KLaola(const myFile &file) {
00026 
00027     smallBlockDepot=0L;
00028     bigBlockDepot=0L;
00029     smallBlockFile=0L;
00030     bbd_list=0L;
00031     ok=true;
00032     m_nodeList.setAutoDelete(true);
00033     //m_currentPath.setAutoDelete(true);
00034 
00035     if( (file.length % 0x200) != 0 ) {
00036         kdError(s_area) << "KLaola::KLaola(): Invalid file size!" << endl;
00037         ok=false;
00038     }
00039     if(ok) {
00040         m_file=file;
00041         maxblock = file.length / 0x200 - 2;
00042         maxSblock=0;  // will be set in readSmallBlockDepot
00043 
00044         if(!parseHeader())
00045             ok=false;
00046         if(ok) {
00047             readBigBlockDepot();
00048             readSmallBlockDepot();
00049             readSmallBlockFile();
00050             readRootList();
00051         }
00052     }
00053     m_currentPath.clear();
00054     testIt();
00055 
00056     // current path=root dirHandle
00057     m_currentPath.clear();
00058     if ( !m_nodeTree.isEmpty() )
00059         m_currentPath.append(m_nodeTree.getFirst()->getFirst()->node);
00060 }
00061 
00062 KLaola::~KLaola() {
00063 
00064     delete [] bigBlockDepot;
00065     bigBlockDepot=0L;
00066     delete [] smallBlockDepot;
00067     smallBlockDepot=0L;
00068     delete [] smallBlockFile;
00069     smallBlockFile=0L;
00070     delete [] bbd_list;
00071     bbd_list=0L;
00072 }
00073 
00074 // Comvert the given list of nodes into a tree.
00075 void KLaola::createTree(const int handle, const short index) {
00076 
00077     Node *node = dynamic_cast<Node *>(m_nodeList.at(handle));
00078     SubTree *subtree;
00079 
00080     TreeNode *tree=new TreeNode;
00081     tree->node=node;
00082     tree->subtree=-1;
00083 
00084     //QString nix="### entering create tree: handle=";
00085     //nix+=QString::number(handle);
00086     //nix+=" index=";
00087     //nix+=QString::number(index);
00088     //kdDebug(s_area) << nix << endl;
00089 
00090     if(node->prevHandle!=-1) {
00091         //kdDebug(s_area) << "create tree: prevHandle" << endl;
00092         createTree(node->prevHandle, index);
00093     }
00094     if(node->dirHandle!=-1) {
00095         subtree=new SubTree;
00096         subtree->setAutoDelete(true);
00097         m_nodeTree.append(subtree);
00098         tree->subtree=m_nodeTree.at();
00099         //kdDebug(s_area) << "create tree: dirHandle" << endl;
00100         createTree(node->dirHandle, tree->subtree);
00101     }
00102     subtree=m_nodeTree.at(index);
00103     //kdDebug(s_area) << "create tree: APPEND " << handle << " tree node " << tree << endl;
00104     subtree->append(tree);
00105     if(node->nextHandle!=-1) {
00106         //kdDebug(s_area) << "create tree: nextHandle" << endl;
00107         createTree(node->nextHandle, index);
00108     }
00109 }
00110 
00111 const KLaola::NodeList KLaola::currentPath() const {
00112     return m_currentPath;
00113 }
00114 
00115 bool KLaola::enterDir(const OLENode *dirHandle) {
00116 
00117     NodeList nodes;
00118     Node *node;
00119 
00120     if(ok) {
00121         nodes = parseCurrentDir();
00122         for (node = dynamic_cast<Node *>(nodes.first()); node; node = dynamic_cast<Node *>(nodes.next()))
00123         {
00124             if(node->m_handle==dirHandle->handle() && node->isDirectory() && !node->deadDir) {
00125                 m_currentPath.append(node);
00126                 return true;
00127             }
00128         }
00129     }
00130     return false;
00131 }
00132 
00133 const KLaola::NodeList KLaola::find(const QString &name, bool onlyCurrentDir) {
00134 
00135     OLENode *node;
00136     NodeList ret;
00137     int i=0;
00138 
00139     if(ok) {
00140         if(!onlyCurrentDir) {
00141             for(node=m_nodeList.first(); node; node=m_nodeList.next()) {
00142                 if(node->name()==name) {
00143                     ret.append(node);
00144                     ++i;
00145                 }
00146             }
00147         }
00148         else {
00149             NodeList list=parseCurrentDir();
00150 
00151             for(node=list.first(); node; node=list.next()) {
00152                 if(node->name()==name) {
00153                     ret.append(node);
00154                     ++i;
00155                 }
00156             }
00157         }
00158     }
00159     return ret;
00160 }
00161 
00162 bool KLaola::leaveDir() {
00163 
00164     if (ok) {
00165         return m_currentPath.removeLast();
00166     }
00167     return false;
00168 }
00169 
00170 int KLaola::nextBigBlock(int pos) const
00171 {
00172 
00173     int x=pos*4;
00174     return ( (bigBlockDepot[x+3] << 24) + (bigBlockDepot[x+2] << 16) +
00175              (bigBlockDepot[x+1] << 8) + bigBlockDepot[x] );
00176 }
00177 
00178 int KLaola::nextSmallBlock(int pos) const
00179 {
00180 
00181     if(smallBlockDepot) {
00182         int x=pos*4;
00183         return ( (smallBlockDepot[x+3] << 24) + (smallBlockDepot[x+2] << 16) +
00184                  (smallBlockDepot[x+1] << 8) + smallBlockDepot[x] );
00185     }
00186     else
00187         return -2;   // Emergency Break :)
00188 }
00189 
00190 bool KLaola::parseHeader() {
00191 
00192     if(qstrncmp((const char*)m_file.data,"\xd0\xcf\x11\xe0\xa1\xb1\x1a\xe1",8 )!=0) {
00193         kdError(s_area) << "KLaola::parseHeader(): Invalid file format (unexpected id in header)!" << endl;
00194         return false;
00195     }
00196 
00197     num_of_bbd_blocks=read32(0x2c);
00198     root_startblock=read32(0x30);
00199     sbd_startblock=read32(0x3c);
00200 
00201     if (num_of_bbd_blocks >= 0x800000) {
00202         kdError(s_area) << "KLaola::parseHeader(): Too many bbd blocks found in header!" << endl;
00203         return false;
00204     }
00205     bbd_list=new unsigned int[num_of_bbd_blocks];
00206 
00207     unsigned int i, j;
00208     for(i=0, j=0; i<num_of_bbd_blocks; ++i, j=j+4) {
00209         bbd_list[i]=read32(0x4c+j);
00210         if (bbd_list[i] >= (0x800000 - 1)) {
00211             kdError(s_area) << "KLaola::parseHeader(): bbd " << i << " offset (" << bbd_list[i] << ") too large" << endl;
00212             return false;
00213         }
00214     }
00215     return true;
00216 }
00217 
00218 KLaola::NodeList KLaola::parseCurrentDir() {
00219 
00220     Node *node;
00221     SubTree *subtree;
00222     NodeList nodeList;
00223     TreeNode *treeNode;
00224     unsigned int i;
00225     bool found;
00226 
00227     if(ok) {
00228         for(i=0, subtree=m_nodeTree.first(); i<m_currentPath.count(); ++i) {
00229             treeNode=subtree->first();
00230             found=false;
00231             do {
00232                 if(treeNode==0) {
00233                     kdError(s_area) << "KLaola::parseCurrentDir(): path seems to be corrupted!" << endl;
00234                     ok=false;
00235                 }
00236                 else if(treeNode->node->handle()==m_currentPath.at(i)->handle() && treeNode->subtree!=-1) {
00237                     found=true;
00238                 }
00239                 else
00240                     treeNode=subtree->next();
00241             } while(!found && ok);
00242             subtree=m_nodeTree.at(treeNode->subtree);
00243         }
00244     }
00245     if(ok) {
00246         for(treeNode=subtree->first(); treeNode!=0; treeNode=subtree->next()) {
00247             node=new Node(*treeNode->node);
00248             node->deadDir = (node->dirHandle==-1 && node->isDirectory());
00249             // this is a strange situation :)
00250             if (node->deadDir)
00251                 kdDebug(s_area) << "ignoring: " << node->describe() << " is empty" << endl;
00252             else
00253                 nodeList.append(node);
00254         }
00255     }
00256     return nodeList;
00257 }
00258 
00259 KLaola::NodeList KLaola::parseRootDir() {
00260 
00261     NodeList tmpNodeList;
00262     NodeList tmp;
00263 
00264     if(ok) {
00265         tmp=m_currentPath;
00266         m_currentPath.clear();      // current path=root dirHandle
00267         m_currentPath.append(m_nodeTree.getFirst()->getFirst()->node);
00268         tmpNodeList=parseCurrentDir();
00269         m_currentPath=tmp;
00270     }
00271     return tmpNodeList;
00272 }
00273 
00274 unsigned char KLaola::read8(int i) const
00275 {
00276     return m_file.data[i];
00277 }
00278 
00279 unsigned short KLaola::read16(int i) const
00280 {
00281     return ( (m_file.data[i+1] << 8) + m_file.data[i] );
00282 }
00283 
00284 unsigned int KLaola::read32(int i) const
00285 {
00286     return ( (read16(i+2) << 16) + read16(i) );
00287 }
00288 
00289 const unsigned char *KLaola::readBBStream(int start, bool setmaxSblock)
00290 {
00291 
00292     int i=0, tmp;
00293     unsigned char *p=0;
00294 
00295     tmp=start;
00296     /* 0x10000 chosen as arbitrary "too many blocks" limit to not loop forver */
00297     while(tmp!=-2 && tmp>=0 && i<0x10000 && tmp<=static_cast<int>(maxblock)) {
00298         ++i;
00299         tmp=nextBigBlock(tmp);
00300     }
00301     if(i!=0) {
00302         p=new unsigned char[i*0x200];
00303         if(setmaxSblock)
00304             maxSblock=i*8-1;
00305         i=0;
00306         tmp=start;
00307         while(tmp!=-2 && tmp>=0 && i<0x10000 && tmp<=static_cast<int>(maxblock)) {
00308             memcpy(&p[i*0x200], &m_file.data[(tmp+1)*0x200], 0x200);
00309             tmp=nextBigBlock(tmp);
00310             ++i;
00311         }
00312     }
00313     return p;
00314 }
00315 
00316 const unsigned char *KLaola::readSBStream(int start) const {
00317 
00318     int i=0, tmp;
00319     unsigned char *p=0;
00320 
00321     tmp=start;
00322     /* 0x10000 chosen as arbitrary "too many blocks" limit to not loop forver */
00323     while(tmp!=-2 && tmp>=0 && i<0x10000 && tmp<=static_cast<int>(maxSblock)) {
00324         ++i;
00325         tmp=nextSmallBlock(tmp);
00326     }
00327     if(i!=0) {
00328         p=new unsigned char[i*0x40];
00329         i=0;
00330         tmp=start;
00331         while(tmp!=-2 && tmp>=0 && i<0x10000 && tmp<=static_cast<int>(maxSblock)) {
00332             memcpy(&p[i*0x40], &smallBlockFile[tmp*0x40], 0x40);
00333             tmp=nextSmallBlock(tmp);
00334             ++i;
00335         }
00336     }
00337     return p;
00338 }
00339 
00340 void KLaola::readBigBlockDepot() {
00341     if (num_of_bbd_blocks >= 0x800000)
00342         return;
00343 
00344     bigBlockDepot=new unsigned char[0x200*num_of_bbd_blocks];
00345     for(unsigned int i=0; i<num_of_bbd_blocks; ++i) {
00346         unsigned int offset = (bbd_list[i]+1)*0x200;
00347         if (offset > m_file.length - 0x200) {
00348             /* attempting to read past end of file */
00349             memset(&bigBlockDepot[i*0x200], 0, 0x200);
00350         }
00351         else {
00352             memcpy(&bigBlockDepot[i*0x200], &m_file.data[offset], 0x200);
00353         }
00354     }
00355 }
00356 
00357 void KLaola::readSmallBlockDepot() {
00358     smallBlockDepot=const_cast<unsigned char*>(readBBStream(sbd_startblock));
00359 }
00360 
00361 void KLaola::readSmallBlockFile() {
00362     smallBlockFile=const_cast<unsigned char*>(readBBStream( read32( (root_startblock+1)*0x200 + 0x74), true));
00363 }
00364 
00365 void KLaola::readRootList() {
00366 
00367     int pos=root_startblock;
00368     int handle=0;
00369 
00370     while(pos!=-2 && pos>=0 && pos<=static_cast<int>(maxblock)) {
00371         for(int i=0; i<4; ++i, ++handle)
00372             readPPSEntry((pos+1)*0x200+0x80*i, handle);
00373         pos=nextBigBlock(pos);
00374     }
00375     SubTree *subtree=new SubTree;
00376     subtree->setAutoDelete(true);
00377     m_nodeTree.append(subtree);
00378 
00379     createTree(0, 0);           // build the tree with a recursive method :)
00380 }
00381 
00382 // Add the given OLE node to the list of nodes.
00383 void KLaola::readPPSEntry(int pos, const int handle) {
00384 
00385     int nameSize = read16(pos + 0x40);
00386 
00387     // Does the PPS Entry seem to be valid?
00388 
00389     if (nameSize)
00390     {
00391         int i;
00392         Node *node = new Node(this);
00393 
00394         // The first character of the name can be a prefix.
00395         node->m_prefix = static_cast<Prefix>(read16(pos));
00396         if (node->m_prefix <= RESERVED_LAST)
00397         {
00398             i = 1;
00399         }
00400         else
00401         {
00402             node->m_prefix = NONE;
00403             i = 0;
00404         }
00405 
00406         // Get the rest of the name.
00407         for (; i < (nameSize / 2) - 1; ++i)
00408         {
00409             QChar tmp;
00410 
00411             tmp = read16(pos + 2 * i);
00412             node->m_name += tmp;
00413         }
00414         node->m_handle = handle;
00415         node->type = static_cast<NodeType>(read8(pos + 0x42));
00416         node->prevHandle = static_cast<int>(read32(pos + 0x44));
00417         node->nextHandle = static_cast<int>(read32(pos + 0x48));
00418         node->dirHandle = static_cast<int>(read32(pos + 0x4C));
00419         node->ts1s = static_cast<int>(read32(pos + 0x64));
00420         node->ts1d = static_cast<int>(read32(pos + 0x68));
00421         node->ts2s = static_cast<int>(read32(pos + 0x6C));
00422         node->ts2d = static_cast<int>(read32(pos + 0x70));
00423         node->sb = read32(pos + 0x74);
00424         node->size = read32(pos + 0x78);
00425         node->deadDir = false;
00426         m_nodeList.append(node);
00427     }
00428 }
00429 
00430 myFile KLaola::stream(const OLENode *node) {
00431 
00432     const Node *realNode = dynamic_cast<const Node *>(node);
00433     const unsigned char *temp;
00434     myFile ret;
00435 
00436     if(ok) {
00437         if(realNode->size>=0x1000)
00438             temp = readBBStream(realNode->sb);
00439         else
00440             temp = readSBStream(realNode->sb);
00441         ret.setRawData(temp, realNode->size);
00442     }
00443     return ret;
00444 }
00445 
00446 myFile KLaola::stream(unsigned handle) {
00447 
00448     OLENode *node;
00449 
00450     node = m_nodeList.at(handle);
00451     return stream(node);
00452 }
00453 
00454 void KLaola::testIt(QString prefix)
00455 {
00456 
00457     NodeList nodes;
00458     OLENode *node;
00459 
00460     nodes = parseCurrentDir();
00461     for (node = nodes.first(); node; node = nodes.next())
00462     {
00463         kdDebug(s_area) << prefix + node->describe() << endl;
00464         if (node->isDirectory())
00465         {
00466             enterDir(node);
00467             testIt(prefix + " ");
00468         }
00469     }
00470 }
00471 
00472 // Return a human-readable description of a stream.
00473 QString KLaola::Node::describe() const
00474 {
00475     QString description;
00476     myFile file;
00477     unsigned i;
00478 
00479     description = QString::number(m_handle) + " " +
00480                     m_name + "(" +
00481                     QString::number(sb) + " " +
00482                     QString::number(size) + " bytes)";
00483     if (isDirectory())
00484         description += ", directory";
00485     switch (m_prefix)
00486     {
00487     case OLE_MANAGED_0:
00488         description += ", OLE_0";
00489         break;
00490     case CLSID:
00491         description += ", CLSID=";
00492         description += readClassStream();
00493         file = m_laola->stream(this);
00494         description += ", ";
00495         for (i = 16; i < file.length; i++)
00496         {
00497             description += QString::number((file.data[i] >> 4) & 0xf, 16);
00498             description += QString::number(file.data[i] & 0xf, 16);
00499         }
00500         description += ", ";
00501         for (i = 16; i < file.length; i++)
00502         {
00503             QChar tmp = file.data[i];
00504 
00505             if (tmp.isPrint())
00506                 description += tmp;
00507             else
00508                 description += '.';
00509         }
00510         break;
00511     case OLE_MANAGED_2:
00512         description += ", OLE_2";
00513         break;
00514     case PARENT_MANAGED:
00515         description += ", parent managed";
00516         break;
00517     case STRUCTURED_STORAGE:
00518         description += ", reserved 0x" + QString::number(m_prefix, 16);
00519         break;
00520     case NONE:
00521         break;
00522     default:
00523         description += ", reserved 0x" + QString::number(m_prefix, 16);
00524         break;
00525     }
00526     return description;
00527 }
00528 
00529 QString KLaola::Node::name() const
00530 {
00531     return m_name;
00532 }
00533 
00534 // See "Associating Code with Storage" in Inside OLE.
00535 QString KLaola::Node::readClassStream() const
00536 {
00537     if (isDirectory())
00538         return QString::null;
00539     if (m_prefix == CLSID)
00540     {
00541         myFile file;
00542         unsigned i;
00543         QString clsid;
00544 
00545         // CLSID format is: 00020900-0000-0000-C000-000000000046
00546         file = m_laola->stream(this);
00547         for (i = 0; i < 4; i++)
00548         {
00549             clsid += QString::number((file.data[i] >> 4) & 0xf, 16);
00550             clsid += QString::number(file.data[i] & 0xf, 16);
00551         }
00552         clsid += '-';
00553         for (; i < 6; i++)
00554         {
00555             clsid += QString::number((file.data[i] >> 4) & 0xf, 16);
00556             clsid += QString::number(file.data[i] & 0xf, 16);
00557         }
00558         clsid += '-';
00559         for (; i < 8; i++)
00560         {
00561             clsid += QString::number((file.data[i] >> 4) & 0xf, 16);
00562             clsid += QString::number(file.data[i] & 0xf, 16);
00563         }
00564         clsid += '-';
00565         for (; i < 10; i++)
00566         {
00567             clsid += QString::number((file.data[i] >> 4) & 0xf, 16);
00568             clsid += QString::number(file.data[i] & 0xf, 16);
00569         }
00570         clsid += '-';
00571         for (; i < 16; i++)
00572         {
00573             clsid += QString::number((file.data[i] >> 4) & 0xf, 16);
00574             clsid += QString::number(file.data[i] & 0xf, 16);
00575         }
00576         return clsid;
00577     }
00578     return QString::null;
00579 }
KDE Home | KDE Accessibility Home | Description of Access Keys