Logo Search packages:      
Sourcecode: kmess version File versions  Download package

msnobject.cpp

/***************************************************************************
                          msnobject.cpp  -  description
                             -------------------
    begin                : Tue Jul 15 2003
    copyright            : (C) 2003 by Mike K. Bennett
    email                : mike@kmess.org
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/

#include "msnobject.h"

#include "crypt/sha1.h"

#include <qfile.h>
#include <qregexp.h>
#include <kmdcodec.h>

#include <kdebug.h>

// The constructor
MsnObject::MsnObject()
 : size_(0)
{
}


00035 MsnObject::MsnObject(const QString &object)
: original_(object)
{
  if( object.isEmpty() )
  {
    kdWarning() << "MsnObject: passed object is empty." << endl;
    return;
  }

  loadObject(object);
}


// Copy constructor
00049 MsnObject::MsnObject(const MsnObject &other)
: creator_(other.creator_)
, friendly_(other.friendly_)
, location_(other.location_)
, original_(other.original_)
, size_(other.size_)
, type_(other.type_)
, sha1d_(other.sha1d_)
, sha1c_(other.sha1c_)
{
  
}


00063 MsnObject::MsnObject(const QString &creator, const QString &location,
               const QString &friendly, MsnObjectType type,
               const QByteArray &fileData)
: creator_(creator)
, friendly_(friendly)
, location_(location)
, type_(type)
{
  size_ = fileData.size();

  // Generate hashes.
  sha1d_= generateDataHash( fileData );
  sha1c_= generateObjectHash();
}





// The destructor
MsnObject::~MsnObject()
{
}



// Parse an attribute from the object string
00090 QString MsnObject::getAttribute( const QString& name, const QString& object )
{
  QString      value;
  int          index;
  QRegExp      findAttribute( name + "=\"([^ ]*)\"" );

  index = findAttribute.search( object );
  if ( index >= 0 )
  {
    if ( findAttribute.numCaptures() > 0 )
    {
      value = findAttribute.capturedTexts()[1];
    }
  }

  return value;
}


// Get the sha1c hash
const QString MsnObject::getContentHash() const
{
  return sha1c_;
}


// Get the object's creator
const QString& MsnObject::getCreator() const
{
  return creator_;
}


// Get the sha1d hash
const QString MsnObject::getDataHash() const
{
  return sha1d_;
}


// Get the object's location
const QString& MsnObject::getLocation() const
{
  return location_;
}


const QString& MsnObject::getFriendly() const
{
  // This is actually base-64 encoded. We must decode it before
  // returning the information.
  return friendly_;
}


// Get the object's size
int MsnObject::getSize() const
{
  return size_;
}



// Get the object's type
MsnObject::MsnObjectType MsnObject::getType() const
{
  return type_;
}



// Use an MSN object descriptor from the server to load data
00162 void MsnObject::loadObject( const QString& object )
{
  creator_  = getAttribute( "Creator", object );
  location_ = getAttribute( "Location", object );
  size_     = getAttribute( "Size", object ).toInt();
  type_     = (MsnObjectType)getAttribute( "Type", object ).toInt();
  sha1d_    = getAttribute("SHA1D", object).utf8();
  sha1c_    = getAttribute("SHA1C", object).utf8();

  // Get friendly name, make sure it's parsed correctly
  QString friendlyEncoded = getAttribute( "Friendly", object );
  if( ! friendlyEncoded.contains("AA") )
  {
    // This could cause crashes.
    kdWarning() << "MsnObject: friendly name is missing a null-terminator (contact=" << creator_ << ")!" << endl;
  }
  else
  {
    QByteArray friendlyUcs2;
    KCodecs::base64Decode( friendlyEncoded.utf8(), friendlyUcs2 );
    friendly_ = QString::fromUcs2( reinterpret_cast<const unsigned short*>(friendlyUcs2.data()) );
  }

  // Bah! Verification is for the weak.
  //if(!verifyObjectHash())
  //{
  //  kdDebug() << "Hash does not match!";
  //  kdDebug() << "MSN6 data for contact: " << creator_ << " " << location_ << " " << size_ << " " << type_ << " " << getFriendly() <<  endl;  //  kdDebug() << "Real sha1c " << sha1c_ << " vs. calculated " << generateObjectHash() << endl;
  //}
}


00194 bool MsnObject::verifyObjectHash() const
{
  // And yes, QCString overloads operator==
  return generateObjectHash() == sha1c_;
}


00201 bool MsnObject::verifyFile( const QString &fileName ) const
{
  QFile file( fileName );
  if( ! file.open( IO_ReadOnly ) )
  {
    kdWarning() << "MsnObject::verifyFile() - unable to open file '" << fileName << "'." << endl;
    return false;
  }

  QByteArray fileData = file.readAll();
  file.close();

  return generateDataHash( fileData ) == sha1d_;
}


00217 const QCString MsnObject::generateDataHash( const QByteArray &fileData ) const
{
  Sha1 sha1;
  QByteArray hash;
  hash.duplicate( (const char *) sha1.calcSha1( (const byte *) fileData.data(), fileData.size() ), 20 );
  return KCodecs::base64Encode(hash);
}

00225 const QCString MsnObject::generateObjectHash() const
{
  // First, we must build the string that needs to be hashed.
  // See amsn protocol.tcl (create_msnobj) for an example.
  // We concatenate field names with their values, in the following order:
  // Creator, Size, Type, Location, Friendly, SHA1D
  QCString baseString= "Creator" + creator_.utf8() +
    "Size" + QString::number(size_).utf8() +
    "Type" + QString::number((int)type_).utf8() +
    "Location" + location_.utf8() +
    "Friendly" + friendly_.utf8() +
    "SHA1D" + sha1d_;

  // Sha1 the thing
  QByteArray shaData(20); // 160 bits of output
  Sha1 sha1;
  shaData.duplicate( (const char *) sha1.calcSha1( (const byte*) baseString.data(), baseString.length() ), 20 );

  // Return the base64-encoded version
  // No worry about linefeeds: should come out to 28 bytes
  return KCodecs::base64Encode(shaData);
}


00249 const QString MsnObject::objectString() const
{
  if( ! original_.isEmpty() )
  {
    return original_;
  }

  QCString friendlyBase64;

  if( friendly_.length() == 0 )
  {
    friendlyBase64 = "AA==";  // null-terminator and padding.
  }
  else
  {
    QByteArray friendlyUcs2;
    int size = friendly_.length() * 2;

    // Copy existing UTF16 string to an QByteArray
    friendlyUcs2.duplicate( reinterpret_cast<const char*>(friendly_.ucs2()), size );
    // Add null-padding
    friendlyUcs2.resize( size + 4 );
    friendlyUcs2[size + 0] = '\0';
    friendlyUcs2[size + 1] = '\0';
    friendlyUcs2[size + 2] = '\0';
    friendlyUcs2[size + 3] = '\0';

    // To BASE64 encoding.
    friendlyBase64 = KCodecs::base64Decode(friendlyUcs2);
  }

  // Create msn object string.
  return QString(
                 "<msnobj Creator=\"%1\" Size=\"%2\" Type=\"%3\""
                 " Location=\"%4\" Friendly=\"%5\""
                 " SHA1D=\"%6\" SHA1C=\"%7\"/>"
                )
                .arg(creator_).arg(size_).arg(type_).arg(location_).arg(friendlyBase64)
                .arg(sha1d_).arg(sha1c_);
}


00291 bool MsnObject::hasChanged( const QString &newObj ) const
{
  // TODO: The QString comparing with != should be overridden to match on QCStrings or char*'s

  // If no hash is present we can't check it, assume it's changed.
  // Will probably just waste a bit of bandwidth.
  if( sha1c_.isEmpty() && sha1d_.isEmpty() )
  {
    return true;
  }

  QString objSha1d = getAttribute( "SHA1D", newObj );

  // The data hash is more important than the full object hash, if it's present, use it
  if( ! sha1d_.isEmpty() && ! objSha1d.isEmpty() )
  {
    // If false the data is the same, the object hasn't changed
    return ( sha1d_ != objSha1d.utf8() );
  }

  // The data hash is not available: use the full object hash

  return ( sha1c_ != getAttribute( "SHA1C", newObj ).utf8() );
}

Generated by  Doxygen 1.6.0   Back to index