<commit_before>/*
    This file is part of libkcal.
    Copyright (c) 2001 Cornelius Schumacher <schumacher@kde.org>

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

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Library General Public License for more details.

    You should have received a copy of the GNU Library General Public License
    along with this library; see the file COPYING.LIB.  If not, write to
    the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
    Boston, MA 02111-1307, USA.
*/

#include <kglobal.h>
#include <klocale.h>
#include <kdebug.h>

#include "calformat.h"

#include "incidence.h"

using namespace KCal;

Incidence::Incidence() :
  IncidenceBase(),
  mRelatedTo(0), mStatus(StatusNone), mSecrecy(SecrecyPublic),
  mPriority(3), mRecurrence(0)
{
  recreate();

  mAlarms.setAutoDelete(true);
  mAttachments.setAutoDelete(true);
}

Incidence::Incidence( const Incidence &i ) : IncidenceBase( i )
{
// TODO: reenable attributes currently commented out.
  mRevision = i.mRevision;
  mCreated = i.mCreated;
  mDescription = i.mDescription;
  mSummary = i.mSummary;
  mCategories = i.mCategories;
//  Incidence *mRelatedTo;          Incidence *mRelatedTo;
  mRelatedTo = 0;
  mRelatedToUid = i.mRelatedToUid;
//  Incidence::List mRelations;    Incidence::List mRelations;
  mExDates = i.mExDates;
  mExDateTimes = i.mExDateTimes;
  mAttachments = i.mAttachments;
  mResources = i.mResources;
  mStatusString = i.mStatusString;
  mStatus = i.mStatus;
  mSecrecy = i.mSecrecy;
  mPriority = i.mPriority;
  mLocation = i.mLocation;

  Alarm::List::ConstIterator it;
  for( it = i.mAlarms.begin(); it != i.mAlarms.end(); ++it ) {
    Alarm *b = new Alarm( **it );
    b->setParent( this );
    mAlarms.append( b );
  }
  mAlarms.setAutoDelete(true);

  if (i.mRecurrence)
    mRecurrence = new Recurrence( *(i.mRecurrence), this );
  else
    mRecurrence = 0;
}

Incidence::~Incidence()
{
    Incidence::List Relations = mRelations;
    List::ConstIterator it;
    for ( it = Relations.begin(); it != Relations.end(); ++it ) {
        if ( (*it)->relatedTo() == this ) (*it)->setRelatedTo( 0 );
    }
    if ( relatedTo() ) relatedTo()->removeRelation( this );

    delete mRecurrence;
}

// A string comparison that considers that null and empty are the same
static bool stringCompare( const QString& s1, const QString& s2 )
{
    if ( s1.isEmpty() && s2.isEmpty() )
        return true;
    return s1 == s2;
}

bool Incidence::operator==( const Incidence& i2 ) const
{
    if( alarms().count() != i2.alarms().count() ) {
        return false; // no need to check further
    }

    Alarm::List::ConstIterator a1 = alarms().begin();
    Alarm::List::ConstIterator a2 = i2.alarms().begin();
    for( ; a1 != alarms().end() && a2 != i2.alarms().end(); ++a1, ++a2 )
        if( **a1 == **a2 )
            continue;
        else {
            return false;
        }

    if ( !IncidenceBase::operator==(i2) )
        return false;

    bool recurrenceEqual = ( mRecurrence == 0 && i2.mRecurrence == 0 );
    if ( !recurrenceEqual )
    {
        recurrenceEqual = mRecurrence != 0 &&
                          i2.mRecurrence != 0 &&
                          *mRecurrence == *i2.mRecurrence;
    }

    return
        recurrenceEqual &&
        created() == i2.created() &&
        stringCompare( description(), i2.description() ) &&
        stringCompare( summary(), i2.summary() ) &&
        categories() == i2.categories() &&
        // no need to compare mRelatedTo
        stringCompare( relatedToUid(), i2.relatedToUid() ) &&
        relations() == i2.relations() &&
        exDates() == i2.exDates() &&
        exDateTimes() == i2.exDateTimes() &&
        attachments() == i2.attachments() &&
        resources() == i2.resources() &&
        mStatus == i2.mStatus &&
        ( mStatus == StatusNone || stringCompare( mStatusString, i2.mStatusString ) ) &&
        secrecy() == i2.secrecy() &&
        priority() == i2.priority() &&
        stringCompare( location(), i2.location() );
}

void Incidence::recreate()
{
  setCreated(QDateTime::currentDateTime());

  setUid(CalFormat::createUniqueId());

  setRevision(0);

  setLastModified(QDateTime::currentDateTime());
}

void Incidence::setReadOnly( bool readOnly )
{
  IncidenceBase::setReadOnly( readOnly );
  if (mRecurrence)
    mRecurrence->setRecurReadOnly(readOnly);
}

void Incidence::setCreated( const QDateTime &created )
{
  if (mReadOnly) return;
  mCreated = created;
}

QDateTime Incidence::created() const
{
  return mCreated;
}

void Incidence::setRevision( int rev )
{
  if (mReadOnly) return;
  mRevision = rev;

  updated();
}

int Incidence::revision() const
{
  return mRevision;
}

void Incidence::setDtStart(const QDateTime &dtStart)
{
  if (mRecurrence)
    mRecurrence->setRecurStart( dtStart );
  IncidenceBase::setDtStart( dtStart );
}

void Incidence::setDescription(const QString &description)
{
  if (mReadOnly) return;
  mDescription = description;
  updated();
}

QString Incidence::description() const
{
  return mDescription;
}

void Incidence::setSummary(const QString &summary)
{
  if (mReadOnly) return;
  mSummary = summary;
  updated();
}

QString Incidence::summary() const
{
  return mSummary;
}

void Incidence::setCategories(const QStringList &categories)
{
  if (mReadOnly) return;
  mCategories = categories;
  updated();
}

// TODO: remove setCategories(QString) function
void Incidence::setCategories(const QString &catStr)
{
  if (mReadOnly) return;
  mCategories.clear();

  if (catStr.isEmpty()) return;

  mCategories = QStringList::split(",",catStr);

  QStringList::Iterator it;
  for(it = mCategories.begin();it != mCategories.end(); ++it) {
    *it = (*it).stripWhiteSpace();
  }

  updated();
}

QStringList Incidence::categories() const
{
  return mCategories;
}

QString Incidence::categoriesStr() const
{
  return mCategories.join(",");
}

void Incidence::setRelatedToUid(const QString &relatedToUid)
{
  if (mReadOnly) return;
  mRelatedToUid = relatedToUid;
}

QString Incidence::relatedToUid() const
{
  return mRelatedToUid;
}

void Incidence::setRelatedTo(Incidence *relatedTo)
{
  if (mReadOnly || mRelatedTo == relatedTo) return;
  if(mRelatedTo)
    mRelatedTo->removeRelation(this);
  mRelatedTo = relatedTo;
  if (mRelatedTo) mRelatedTo->addRelation(this);
}

Incidence *Incidence::relatedTo() const
{
  return mRelatedTo;
}

Incidence::List Incidence::relations() const
{
  return mRelations;
}

void Incidence::addRelation( Incidence *event )
{
  if ( mRelations.find( event ) == mRelations.end() ) {
    mRelations.append( event );
    updated();
  }
}

void Incidence::removeRelation(Incidence *event)
{
  mRelations.removeRef(event);
//  if (event->getRelatedTo() == this) event->setRelatedTo(0);
}

bool Incidence::recursOn(const QDate &qd) const
{
  return (mRecurrence && mRecurrence->recursOnPure(qd) && !isException(qd));
}

bool Incidence::recursAt(const QDateTime &qdt) const
{
  return (mRecurrence && mRecurrence->recursAtPure(qdt) && !isException(qdt.date()) && !isException(qdt));
}

void Incidence::setExDates(const DateList &exDates)
{
  if (mReadOnly) return;
  mExDates = exDates;
  updated();
}

void Incidence::setExDateTimes(const DateTimeList &exDateTimes)
{
  if (mReadOnly) return;
  mExDateTimes = exDateTimes;
  updated();
}

void Incidence::addExDate(const QDate &date)
{
  if (mReadOnly) return;
  mExDates.append(date);
  updated();
}

void Incidence::addExDateTime(const QDateTime &dateTime)
{
  if (mReadOnly) return;
  mExDateTimes.append(dateTime);
  updated();
}

DateList Incidence::exDates() const
{
  return mExDates;
}

DateTimeList Incidence::exDateTimes() const
{
  return mExDateTimes;
}

bool Incidence::isException(const QDate &date) const
{
  DateList::ConstIterator it;
  for( it = mExDates.begin(); it != mExDates.end(); ++it ) {
    if ( (*it) == date ) {
      return true;
    }
  }

  return false;
}

bool Incidence::isException(const QDateTime &dateTime) const
{
  DateTimeList::ConstIterator it;
  for( it = mExDateTimes.begin(); it != mExDateTimes.end(); ++it ) {
    if ( (*it) == dateTime ) {
      return true;
    }
  }

  return false;
}

void Incidence::addAttachment(Attachment *attachment)
{
  if (mReadOnly || !attachment) return;
  mAttachments.append(attachment);
  updated();
}

void Incidence::deleteAttachment(Attachment *attachment)
{
  mAttachments.removeRef(attachment);
}

void Incidence::deleteAttachments( const QString &mime )
{
  Attachment::List::Iterator it = mAttachments.begin();
  while( it != mAttachments.end() ) {
    if ( (*it)->mimeType() == mime ) mAttachments.remove( it );
    else ++it;
  }
}

Attachment::List Incidence::attachments() const
{
  return mAttachments;
}

Attachment::List Incidence::attachments(const QString& mime) const
{
  Attachment::List attachments;
  Attachment::List::ConstIterator it;
  for( it = mAttachments.begin(); it != mAttachments.end(); ++it ) {
    if ( (*it)->mimeType() == mime ) attachments.append( *it );
  }

  return attachments;
}

void Incidence::clearAttachments()
{
  mAttachments.clear();
}

void Incidence::setResources(const QStringList &resources)
{
  if (mReadOnly) return;
  mResources = resources;
  updated();
}

QStringList Incidence::resources() const
{
  return mResources;
}

void Incidence::setPriority(int priority)
{
  if (mReadOnly) return;
  mPriority = priority;
  updated();
}

int Incidence::priority() const
{
  return mPriority;
}

void Incidence::setStatus(Incidence::Status status)
{
  if (mReadOnly || status == StatusX) return;
  mStatus = status;
  mStatusString = QString::null;
  updated();
}

void Incidence::setCustomStatus(const QString &status)
{
  if (mReadOnly) return;
  mStatus = status.isEmpty() ? StatusNone : StatusX;
  mStatusString = status;
  updated();
}

Incidence::Status Incidence::status() const
{
  return mStatus;
}

QString Incidence::statusStr() const
{
  if (mStatus == StatusX)
    return mStatusString;
  return statusName(mStatus);
}

QString Incidence::statusName(Incidence::Status status)
{
  switch (status) {
    case StatusTentative:    return i18n("Tentative");
    case StatusConfirmed:    return i18n("Confirmed");
    case StatusCompleted:    return i18n("Completed");
    case StatusNeedsAction:  return i18n("Needs-Action");
    case StatusCanceled:     return i18n("Canceled");
    case StatusInProcess:    return i18n("In-Process");
    case StatusDraft:        return i18n("Draft");
    case StatusFinal:        return i18n("Final");
    case StatusX:
    case StatusNone:
    default:                 return QString::null;
  }
}

void Incidence::setSecrecy(int sec)
{
  if (mReadOnly) return;
  mSecrecy = sec;
  updated();
}

int Incidence::secrecy() const
{
  return mSecrecy;
}

QString Incidence::secrecyStr() const
{
  return secrecyName(mSecrecy);
}

QString Incidence::secrecyName(int secrecy)
{
  switch (secrecy) {
    case SecrecyPublic:
      return i18n("Public");
    case SecrecyPrivate:
      return i18n("Private");
    case SecrecyConfidential:
      return i18n("Confidential");
    default:
      return i18n("Undefined");
  }
}

QStringList Incidence::secrecyList()
{
  QStringList list;
  list << secrecyName(SecrecyPublic);
  list << secrecyName(SecrecyPrivate);
  list << secrecyName(SecrecyConfidential);

  return list;
}

const Alarm::List &Incidence::alarms() const
{
  return mAlarms;
}

Alarm* Incidence::newAlarm()
{
  Alarm* alarm = new Alarm(this);
  mAlarms.append(alarm);
//  updated();
  return alarm;
}

void Incidence::addAlarm(Alarm *alarm)
{
  mAlarms.append(alarm);
  updated();
}

void Incidence::removeAlarm(Alarm *alarm)
{
  mAlarms.removeRef(alarm);
  updated();
}

void Incidence::clearAlarms()
{
  mAlarms.clear();
  updated();
}

bool Incidence::isAlarmEnabled() const
{
  Alarm::List::ConstIterator it;
  for( it = mAlarms.begin(); it != mAlarms.end(); ++it ) {
    if ( (*it)->enabled() ) return true;
  }
  return false;
}

Recurrence *Incidence::recurrence() const
{
  if (!mRecurrence)
  {
    const_cast<KCal::Incidence*>(this)->mRecurrence = new Recurrence(const_cast<KCal::Incidence*>(this));
    mRecurrence->setRecurReadOnly(mReadOnly);
    mRecurrence->setRecurStart(dtStart());
  }

  return mRecurrence;
}

void Incidence::setLocation(const QString &location)
{
  if (mReadOnly) return;
  mLocation = location;
  updated();
}

QString Incidence::location() const
{
  return mLocation;
}

ushort Incidence::doesRecur() const
{
  if ( mRecurrence ) return mRecurrence->doesRecur();
  else return Recurrence::rNone;
}
<commit_msg>If we clone an incidence, we also need to reset the pilot id. Otherwise we'll have two items with the same pilot id, which will prevent incidences copied in korganizer to be synced (unless you do a first sync).<commit_after>/*
    This file is part of libkcal.
    Copyright (c) 2001 Cornelius Schumacher <schumacher@kde.org>

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

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Library General Public License for more details.

    You should have received a copy of the GNU Library General Public License
    along with this library; see the file COPYING.LIB.  If not, write to
    the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
    Boston, MA 02111-1307, USA.
*/

#include <kglobal.h>
#include <klocale.h>
#include <kdebug.h>

#include "calformat.h"

#include "incidence.h"

using namespace KCal;

Incidence::Incidence() :
  IncidenceBase(),
  mRelatedTo(0), mStatus(StatusNone), mSecrecy(SecrecyPublic),
  mPriority(3), mRecurrence(0)
{
  recreate();

  mAlarms.setAutoDelete(true);
  mAttachments.setAutoDelete(true);
}

Incidence::Incidence( const Incidence &i ) : IncidenceBase( i )
{
// TODO: reenable attributes currently commented out.
  mRevision = i.mRevision;
  mCreated = i.mCreated;
  mDescription = i.mDescription;
  mSummary = i.mSummary;
  mCategories = i.mCategories;
//  Incidence *mRelatedTo;          Incidence *mRelatedTo;
  mRelatedTo = 0;
  mRelatedToUid = i.mRelatedToUid;
//  Incidence::List mRelations;    Incidence::List mRelations;
  mExDates = i.mExDates;
  mExDateTimes = i.mExDateTimes;
  mAttachments = i.mAttachments;
  mResources = i.mResources;
  mStatusString = i.mStatusString;
  mStatus = i.mStatus;
  mSecrecy = i.mSecrecy;
  mPriority = i.mPriority;
  mLocation = i.mLocation;

  Alarm::List::ConstIterator it;
  for( it = i.mAlarms.begin(); it != i.mAlarms.end(); ++it ) {
    Alarm *b = new Alarm( **it );
    b->setParent( this );
    mAlarms.append( b );
  }
  mAlarms.setAutoDelete(true);

  if (i.mRecurrence)
    mRecurrence = new Recurrence( *(i.mRecurrence), this );
  else
    mRecurrence = 0;
}

Incidence::~Incidence()
{
    Incidence::List Relations = mRelations;
    List::ConstIterator it;
    for ( it = Relations.begin(); it != Relations.end(); ++it ) {
        if ( (*it)->relatedTo() == this ) (*it)->setRelatedTo( 0 );
    }
    if ( relatedTo() ) relatedTo()->removeRelation( this );

    delete mRecurrence;
}

// A string comparison that considers that null and empty are the same
static bool stringCompare( const QString& s1, const QString& s2 )
{
    if ( s1.isEmpty() && s2.isEmpty() )
        return true;
    return s1 == s2;
}

bool Incidence::operator==( const Incidence& i2 ) const
{
    if( alarms().count() != i2.alarms().count() ) {
        return false; // no need to check further
    }

    Alarm::List::ConstIterator a1 = alarms().begin();
    Alarm::List::ConstIterator a2 = i2.alarms().begin();
    for( ; a1 != alarms().end() && a2 != i2.alarms().end(); ++a1, ++a2 )
        if( **a1 == **a2 )
            continue;
        else {
            return false;
        }

    if ( !IncidenceBase::operator==(i2) )
        return false;

    bool recurrenceEqual = ( mRecurrence == 0 && i2.mRecurrence == 0 );
    if ( !recurrenceEqual )
    {
        recurrenceEqual = mRecurrence != 0 &&
                          i2.mRecurrence != 0 &&
                          *mRecurrence == *i2.mRecurrence;
    }

    return
        recurrenceEqual &&
        created() == i2.created() &&
        stringCompare( description(), i2.description() ) &&
        stringCompare( summary(), i2.summary() ) &&
        categories() == i2.categories() &&
        // no need to compare mRelatedTo
        stringCompare( relatedToUid(), i2.relatedToUid() ) &&
        relations() == i2.relations() &&
        exDates() == i2.exDates() &&
        exDateTimes() == i2.exDateTimes() &&
        attachments() == i2.attachments() &&
        resources() == i2.resources() &&
        mStatus == i2.mStatus &&
        ( mStatus == StatusNone || stringCompare( mStatusString, i2.mStatusString ) ) &&
        secrecy() == i2.secrecy() &&
        priority() == i2.priority() &&
        stringCompare( location(), i2.location() );
}

void Incidence::recreate()
{
  setCreated(QDateTime::currentDateTime());

  setUid(CalFormat::createUniqueId());

  setRevision(0);

  setLastModified(QDateTime::currentDateTime());
  setPilotId( 0 );
  setSyncStatus( SYNCNONE );
}

void Incidence::setReadOnly( bool readOnly )
{
  IncidenceBase::setReadOnly( readOnly );
  if (mRecurrence)
    mRecurrence->setRecurReadOnly(readOnly);
}

void Incidence::setCreated( const QDateTime &created )
{
  if (mReadOnly) return;
  mCreated = created;
}

QDateTime Incidence::created() const
{
  return mCreated;
}

void Incidence::setRevision( int rev )
{
  if (mReadOnly) return;
  mRevision = rev;

  updated();
}

int Incidence::revision() const
{
  return mRevision;
}

void Incidence::setDtStart(const QDateTime &dtStart)
{
  if (mRecurrence)
    mRecurrence->setRecurStart( dtStart );
  IncidenceBase::setDtStart( dtStart );
}

void Incidence::setDescription(const QString &description)
{
  if (mReadOnly) return;
  mDescription = description;
  updated();
}

QString Incidence::description() const
{
  return mDescription;
}

void Incidence::setSummary(const QString &summary)
{
  if (mReadOnly) return;
  mSummary = summary;
  updated();
}

QString Incidence::summary() const
{
  return mSummary;
}

void Incidence::setCategories(const QStringList &categories)
{
  if (mReadOnly) return;
  mCategories = categories;
  updated();
}

// TODO: remove setCategories(QString) function
void Incidence::setCategories(const QString &catStr)
{
  if (mReadOnly) return;
  mCategories.clear();

  if (catStr.isEmpty()) return;

  mCategories = QStringList::split(",",catStr);

  QStringList::Iterator it;
  for(it = mCategories.begin();it != mCategories.end(); ++it) {
    *it = (*it).stripWhiteSpace();
  }

  updated();
}

QStringList Incidence::categories() const
{
  return mCategories;
}

QString Incidence::categoriesStr() const
{
  return mCategories.join(",");
}

void Incidence::setRelatedToUid(const QString &relatedToUid)
{
  if (mReadOnly) return;
  mRelatedToUid = relatedToUid;
}

QString Incidence::relatedToUid() const
{
  return mRelatedToUid;
}

void Incidence::setRelatedTo(Incidence *relatedTo)
{
  if (mReadOnly || mRelatedTo == relatedTo) return;
  if(mRelatedTo)
    mRelatedTo->removeRelation(this);
  mRelatedTo = relatedTo;
  if (mRelatedTo) mRelatedTo->addRelation(this);
}

Incidence *Incidence::relatedTo() const
{
  return mRelatedTo;
}

Incidence::List Incidence::relations() const
{
  return mRelations;
}

void Incidence::addRelation( Incidence *event )
{
  if ( mRelations.find( event ) == mRelations.end() ) {
    mRelations.append( event );
    updated();
  }
}

void Incidence::removeRelation(Incidence *event)
{
  mRelations.removeRef(event);
//  if (event->getRelatedTo() == this) event->setRelatedTo(0);
}

bool Incidence::recursOn(const QDate &qd) const
{
  return (mRecurrence && mRecurrence->recursOnPure(qd) && !isException(qd));
}

bool Incidence::recursAt(const QDateTime &qdt) const
{
  return (mRecurrence && mRecurrence->recursAtPure(qdt) && !isException(qdt.date()) && !isException(qdt));
}

void Incidence::setExDates(const DateList &exDates)
{
  if (mReadOnly) return;
  mExDates = exDates;
  updated();
}

void Incidence::setExDateTimes(const DateTimeList &exDateTimes)
{
  if (mReadOnly) return;
  mExDateTimes = exDateTimes;
  updated();
}

void Incidence::addExDate(const QDate &date)
{
  if (mReadOnly) return;
  mExDates.append(date);
  updated();
}

void Incidence::addExDateTime(const QDateTime &dateTime)
{
  if (mReadOnly) return;
  mExDateTimes.append(dateTime);
  updated();
}

DateList Incidence::exDates() const
{
  return mExDates;
}

DateTimeList Incidence::exDateTimes() const
{
  return mExDateTimes;
}

bool Incidence::isException(const QDate &date) const
{
  DateList::ConstIterator it;
  for( it = mExDates.begin(); it != mExDates.end(); ++it ) {
    if ( (*it) == date ) {
      return true;
    }
  }

  return false;
}

bool Incidence::isException(const QDateTime &dateTime) const
{
  DateTimeList::ConstIterator it;
  for( it = mExDateTimes.begin(); it != mExDateTimes.end(); ++it ) {
    if ( (*it) == dateTime ) {
      return true;
    }
  }

  return false;
}

void Incidence::addAttachment(Attachment *attachment)
{
  if (mReadOnly || !attachment) return;
  mAttachments.append(attachment);
  updated();
}

void Incidence::deleteAttachment(Attachment *attachment)
{
  mAttachments.removeRef(attachment);
}

void Incidence::deleteAttachments( const QString &mime )
{
  Attachment::List::Iterator it = mAttachments.begin();
  while( it != mAttachments.end() ) {
    if ( (*it)->mimeType() == mime ) mAttachments.remove( it );
    else ++it;
  }
}

Attachment::List Incidence::attachments() const
{
  return mAttachments;
}

Attachment::List Incidence::attachments(const QString& mime) const
{
  Attachment::List attachments;
  Attachment::List::ConstIterator it;
  for( it = mAttachments.begin(); it != mAttachments.end(); ++it ) {
    if ( (*it)->mimeType() == mime ) attachments.append( *it );
  }

  return attachments;
}

void Incidence::clearAttachments()
{
  mAttachments.clear();
}

void Incidence::setResources(const QStringList &resources)
{
  if (mReadOnly) return;
  mResources = resources;
  updated();
}

QStringList Incidence::resources() const
{
  return mResources;
}

void Incidence::setPriority(int priority)
{
  if (mReadOnly) return;
  mPriority = priority;
  updated();
}

int Incidence::priority() const
{
  return mPriority;
}

void Incidence::setStatus(Incidence::Status status)
{
  if (mReadOnly || status == StatusX) return;
  mStatus = status;
  mStatusString = QString::null;
  updated();
}

void Incidence::setCustomStatus(const QString &status)
{
  if (mReadOnly) return;
  mStatus = status.isEmpty() ? StatusNone : StatusX;
  mStatusString = status;
  updated();
}

Incidence::Status Incidence::status() const
{
  return mStatus;
}

QString Incidence::statusStr() const
{
  if (mStatus == StatusX)
    return mStatusString;
  return statusName(mStatus);
}

QString Incidence::statusName(Incidence::Status status)
{
  switch (status) {
    case StatusTentative:    return i18n("Tentative");
    case StatusConfirmed:    return i18n("Confirmed");
    case StatusCompleted:    return i18n("Completed");
    case StatusNeedsAction:  return i18n("Needs-Action");
    case StatusCanceled:     return i18n("Canceled");
    case StatusInProcess:    return i18n("In-Process");
    case StatusDraft:        return i18n("Draft");
    case StatusFinal:        return i18n("Final");
    case StatusX:
    case StatusNone:
    default:                 return QString::null;
  }
}

void Incidence::setSecrecy(int sec)
{
  if (mReadOnly) return;
  mSecrecy = sec;
  updated();
}

int Incidence::secrecy() const
{
  return mSecrecy;
}

QString Incidence::secrecyStr() const
{
  return secrecyName(mSecrecy);
}

QString Incidence::secrecyName(int secrecy)
{
  switch (secrecy) {
    case SecrecyPublic:
      return i18n("Public");
    case SecrecyPrivate:
      return i18n("Private");
    case SecrecyConfidential:
      return i18n("Confidential");
    default:
      return i18n("Undefined");
  }
}

QStringList Incidence::secrecyList()
{
  QStringList list;
  list << secrecyName(SecrecyPublic);
  list << secrecyName(SecrecyPrivate);
  list << secrecyName(SecrecyConfidential);

  return list;
}

const Alarm::List &Incidence::alarms() const
{
  return mAlarms;
}

Alarm* Incidence::newAlarm()
{
  Alarm* alarm = new Alarm(this);
  mAlarms.append(alarm);
//  updated();
  return alarm;
}

void Incidence::addAlarm(Alarm *alarm)
{
  mAlarms.append(alarm);
  updated();
}

void Incidence::removeAlarm(Alarm *alarm)
{
  mAlarms.removeRef(alarm);
  updated();
}

void Incidence::clearAlarms()
{
  mAlarms.clear();
  updated();
}

bool Incidence::isAlarmEnabled() const
{
  Alarm::List::ConstIterator it;
  for( it = mAlarms.begin(); it != mAlarms.end(); ++it ) {
    if ( (*it)->enabled() ) return true;
  }
  return false;
}

Recurrence *Incidence::recurrence() const
{
  if (!mRecurrence)
  {
    const_cast<KCal::Incidence*>(this)->mRecurrence = new Recurrence(const_cast<KCal::Incidence*>(this));
    mRecurrence->setRecurReadOnly(mReadOnly);
    mRecurrence->setRecurStart(dtStart());
  }

  return mRecurrence;
}

void Incidence::setLocation(const QString &location)
{
  if (mReadOnly) return;
  mLocation = location;
  updated();
}

QString Incidence::location() const
{
  return mLocation;
}

ushort Incidence::doesRecur() const
{
  if ( mRecurrence ) return mRecurrence->doesRecur();
  else return Recurrence::rNone;
}
<|endoftext|>


<commit_before>/*
    This file is part of libkcal.

    Copyright (c) 2001,2004 Cornelius Schumacher <schumacher@kde.org>
    Copyright (C) 2004 Reinhold Kainhofer <reinhold@kainhofer.com>

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

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Library General Public License for more details.

    You should have received a copy of the GNU Library General Public License
    along with this library; see the file COPYING.LIB.  If not, write to
    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
    Boston, MA 02110-1301, USA.
*/

#include <klocale.h>
#include <kdebug.h>
#include <kstandarddirs.h>

#include "event.h"
#include "todo.h"
#include "freebusy.h"
#include "icalformat.h"
#include "calendar.h"
#include "freebusycache.h"

#include "scheduler.h"

using namespace KCal;

ScheduleMessage::ScheduleMessage(IncidenceBase *incidence,int method,ScheduleMessage::Status status)
{
  mIncidence = incidence;
  mMethod = method;
  mStatus = status;
}

QString ScheduleMessage::statusName(ScheduleMessage::Status status)
{
  switch (status) {
    case PublishUpdate:
      return i18n("Updated Publish");
    case PublishNew:
      return i18n("Publish");
    case Obsolete:
      return i18n("Obsolete");
    case RequestNew:
      return i18n("New Request");
    case RequestUpdate:
      return i18n("Updated Request");
    default:
      return i18n("Unknown Status: %1").arg(QString::number(status));
  }
}

struct Scheduler::Private
{
  Private() : mFreeBusyCache( 0 ) {}

  FreeBusyCache *mFreeBusyCache;
};

Scheduler::Scheduler(Calendar *calendar)
{
  mCalendar = calendar;
  mFormat = new ICalFormat();
  mFormat->setTimeZone( calendar->timeZoneId(), !calendar->isLocalTime() );

  d = new Private;
}

Scheduler::~Scheduler()
{
  delete d;

  delete mFormat;
}

void Scheduler::setFreeBusyCache( FreeBusyCache *c )
{
  d->mFreeBusyCache = c;
}

FreeBusyCache *Scheduler::freeBusyCache() const
{
  return d->mFreeBusyCache;
}

bool Scheduler::acceptTransaction(IncidenceBase *incidence,Method method,ScheduleMessage::Status status)
{
  kdDebug(5800) << "Scheduler::acceptTransaction, method="
                << methodName( method ) << endl;

  switch (method) {
    case Publish:
      return acceptPublish(incidence, status, method);
    case Request:
      return acceptRequest(incidence, status);
    case Add:
      return acceptAdd(incidence, status);
    case Cancel:
      return acceptCancel(incidence, status);
    case Declinecounter:
      return acceptDeclineCounter(incidence, status);
    case Reply:
      return acceptReply(incidence, status, method);
    case Refresh:
      return acceptRefresh(incidence, status);
    case Counter:
      return acceptCounter(incidence, status);
    default:
      break;
  }
  deleteTransaction(incidence);
  return false;
}

QString Scheduler::methodName(Method method)
{
  switch (method) {
    case Publish:
      return QString::fromLatin1("Publish");
    case Request:
      return QString::fromLatin1("Request");
    case Refresh:
      return QString::fromLatin1("Refresh");
    case Cancel:
      return QString::fromLatin1("Cancel");
    case Add:
      return QString::fromLatin1("Add");
    case Reply:
      return QString::fromLatin1("Reply");
    case Counter:
      return QString::fromLatin1("Counter");
    case Declinecounter:
      return QString::fromLatin1("Decline Counter");
    default:
      return QString::fromLatin1("Unknown");
  }
}

QString Scheduler::translatedMethodName(Method method)
{
  switch (method) {
    case Publish:
      return i18n("Publish");
    case Request:
      return i18n("Request");
    case Refresh:
      return i18n("Refresh");
    case Cancel:
      return i18n("Cancel");
    case Add:
      return i18n("Add");
    case Reply:
      return i18n("Reply");
    case Counter:
      return i18n("counter proposal","Counter");
    case Declinecounter:
      return i18n("decline counter proposal","Decline Counter");
    default:
      return i18n("Unknown");
  }
}

bool Scheduler::deleteTransaction(IncidenceBase *)
{
  return true;
}

bool Scheduler::acceptPublish( IncidenceBase *newIncBase,
                               ScheduleMessage::Status status, Method method )
{
  if( newIncBase->type() == "FreeBusy" ) {
    return acceptFreeBusy( newIncBase, method );
  }

  bool res = false;
  kdDebug(5800) << "Scheduler::acceptPublish, status="
            << ScheduleMessage::statusName( status ) << endl;
  Incidence *newInc = static_cast<Incidence *>( newIncBase );
  Incidence *calInc = mCalendar->incidence( newIncBase->uid() );
  switch ( status ) {
    case ScheduleMessage::Unknown:
    case ScheduleMessage::PublishNew:
    case ScheduleMessage::PublishUpdate:
      res = true;
      if ( calInc ) {
        if ( (newInc->revision() > calInc->revision()) ||
             (newInc->revision() == calInc->revision() &&
               newInc->lastModified() > calInc->lastModified() ) ) {
          mCalendar->deleteIncidence( calInc );
        } else
          res = false;
      }
      if ( res )
        mCalendar->addIncidence( newInc );
      break;
    case ScheduleMessage::Obsolete:
      res = true;
      break;
    default:
      break;
  }
  deleteTransaction( newIncBase );
  return res;
}

bool Scheduler::acceptRequest(IncidenceBase *newIncBase, ScheduleMessage::Status /* status */)
{
  if (newIncBase->type()=="FreeBusy") {
    // reply to this request is handled in korganizer's incomingdialog
    return true;
  }
  Incidence *newInc = dynamic_cast<Incidence *>( newIncBase );
  if ( newInc ) {
    bool res = true;
    Incidence *exInc = mCalendar->incidenceFromSchedulingID( newIncBase->uid() );
    if ( exInc ) {
      res = false;
      if ( (newInc->revision() > exInc->revision()) ||
           (newInc->revision() == exInc->revision() &&
             newInc->lastModified()>exInc->lastModified()) ) {
        mCalendar->deleteIncidence( exInc );
        res = true;
      }
    }
    if ( res ) {
      // Move the uid to be the schedulingID and make a unique UID
      newInc->setSchedulingID( newInc->uid() );
      newInc->setUid( CalFormat::createUniqueId() );

      mCalendar->addIncidence(newInc);
    }
    deleteTransaction( newIncBase );
    return res;
  }
  return false;
}

bool Scheduler::acceptAdd(IncidenceBase *incidence,ScheduleMessage::Status /* status */)
{
  deleteTransaction(incidence);
  return false;
}

bool Scheduler::acceptCancel(IncidenceBase *incidence,ScheduleMessage::Status /* status */)
{
  bool ret = false;
  const IncidenceBase *toDelete = mCalendar->incidenceFromSchedulingID( incidence->uid() );
  Event *even = mCalendar->event(toDelete->uid());
  if (even) {
    mCalendar->deleteEvent(even);
    ret = true;
  } else {
    Todo *todo = mCalendar->todo(toDelete->uid());
    if (todo) {
      mCalendar->deleteTodo(todo);
      ret = true;
    }
  }
  deleteTransaction(incidence);
  return ret;
}

bool Scheduler::acceptDeclineCounter(IncidenceBase *incidence,ScheduleMessage::Status /* status */)
{
  deleteTransaction(incidence);
  return false;
}

//bool Scheduler::acceptFreeBusy(Incidence *incidence,ScheduleMessage::Status status)
//{
//  deleteTransaction(incidence);
//  return false;
//}

bool Scheduler::acceptReply(IncidenceBase *incidence,ScheduleMessage::Status /* status */, Method method)
{
  if(incidence->type()=="FreeBusy") {
    return acceptFreeBusy(incidence, method);
  }
  bool ret = false;
  Event *ev = mCalendar->event(incidence->uid());
  Todo *to = mCalendar->todo(incidence->uid());
  if (ev || to) {
    //get matching attendee in calendar
    kdDebug(5800) << "Scheduler::acceptTransaction match found!" << endl;
    Attendee::List attendeesIn = incidence->attendees();
    Attendee::List attendeesEv;
    if (ev) attendeesEv = ev->attendees();
    if (to) attendeesEv = to->attendees();
    Attendee::List::ConstIterator inIt;
    Attendee::List::ConstIterator evIt;
    for ( inIt = attendeesIn.begin(); inIt != attendeesIn.end(); ++inIt ) {
      Attendee *attIn = *inIt;
      for ( evIt = attendeesEv.begin(); evIt != attendeesEv.end(); ++evIt ) {
        Attendee *attEv = *evIt;
        if (attIn->email().lower()==attEv->email().lower()) {
          //update attendee-info
          kdDebug(5800) << "Scheduler::acceptTransaction update attendee" << endl;
          attEv->setStatus(attIn->status());
          ret = true;
        }
      }
    }
    if ( ret ) {
      // We set at least one of the attendees, so the incidence changed
      // Note: This should not result in a sequence number bump
      if ( ev )
        ev->updated();
      else if ( to )
        to->updated();
    }
    if ( to ) {
      // for VTODO a REPLY can be used to update the completion status of
      // a task. see RFC2446 3.4.3
      Todo *update = dynamic_cast<Todo*> ( incidence );
      Q_ASSERT( update );
      if ( update && ( to->percentComplete() != update->percentComplete() ) ) {
        to->setPercentComplete( update->percentComplete() );
        to->updated();
      }
    }
  } else
    kdError(5800) << "No incidence for scheduling\n";
  if (ret) deleteTransaction(incidence);
  return ret;
}

bool Scheduler::acceptRefresh(IncidenceBase *incidence,ScheduleMessage::Status /* status */)
{
  // handled in korganizer's IncomingDialog
  deleteTransaction(incidence);
  return false;
}

bool Scheduler::acceptCounter(IncidenceBase *incidence,ScheduleMessage::Status /* status */)
{
  deleteTransaction(incidence);
  return false;
}

bool Scheduler::acceptFreeBusy(IncidenceBase *incidence, Method method)
{
  if ( !d->mFreeBusyCache ) {
    kdError() << "KCal::Scheduler: no FreeBusyCache." << endl;
    return false;
  }

  FreeBusy *freebusy = static_cast<FreeBusy *>(incidence);

  kdDebug(5800) << "acceptFreeBusy:: freeBusyDirName: " << freeBusyDir() << endl;

  Person from;
  if(method == Scheduler::Publish) {
    from = freebusy->organizer();
  }
  if((method == Scheduler::Reply) && (freebusy->attendeeCount() == 1)) {
    Attendee *attendee = freebusy->attendees().first();
    from = attendee->email();
  }

  if ( !d->mFreeBusyCache->saveFreeBusy( freebusy, from ) ) return false;

  deleteTransaction(incidence);
  return true;
}
<commit_msg>Don't crash when non-existing incidences are canceled.<commit_after>/*
    This file is part of libkcal.

    Copyright (c) 2001,2004 Cornelius Schumacher <schumacher@kde.org>
    Copyright (C) 2004 Reinhold Kainhofer <reinhold@kainhofer.com>

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

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Library General Public License for more details.

    You should have received a copy of the GNU Library General Public License
    along with this library; see the file COPYING.LIB.  If not, write to
    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
    Boston, MA 02110-1301, USA.
*/

#include <klocale.h>
#include <kdebug.h>
#include <kstandarddirs.h>

#include "event.h"
#include "todo.h"
#include "freebusy.h"
#include "icalformat.h"
#include "calendar.h"
#include "freebusycache.h"

#include "scheduler.h"

using namespace KCal;

ScheduleMessage::ScheduleMessage(IncidenceBase *incidence,int method,ScheduleMessage::Status status)
{
  mIncidence = incidence;
  mMethod = method;
  mStatus = status;
}

QString ScheduleMessage::statusName(ScheduleMessage::Status status)
{
  switch (status) {
    case PublishUpdate:
      return i18n("Updated Publish");
    case PublishNew:
      return i18n("Publish");
    case Obsolete:
      return i18n("Obsolete");
    case RequestNew:
      return i18n("New Request");
    case RequestUpdate:
      return i18n("Updated Request");
    default:
      return i18n("Unknown Status: %1").arg(QString::number(status));
  }
}

struct Scheduler::Private
{
  Private() : mFreeBusyCache( 0 ) {}

  FreeBusyCache *mFreeBusyCache;
};

Scheduler::Scheduler(Calendar *calendar)
{
  mCalendar = calendar;
  mFormat = new ICalFormat();
  mFormat->setTimeZone( calendar->timeZoneId(), !calendar->isLocalTime() );

  d = new Private;
}

Scheduler::~Scheduler()
{
  delete d;

  delete mFormat;
}

void Scheduler::setFreeBusyCache( FreeBusyCache *c )
{
  d->mFreeBusyCache = c;
}

FreeBusyCache *Scheduler::freeBusyCache() const
{
  return d->mFreeBusyCache;
}

bool Scheduler::acceptTransaction(IncidenceBase *incidence,Method method,ScheduleMessage::Status status)
{
  kdDebug(5800) << "Scheduler::acceptTransaction, method="
                << methodName( method ) << endl;

  switch (method) {
    case Publish:
      return acceptPublish(incidence, status, method);
    case Request:
      return acceptRequest(incidence, status);
    case Add:
      return acceptAdd(incidence, status);
    case Cancel:
      return acceptCancel(incidence, status);
    case Declinecounter:
      return acceptDeclineCounter(incidence, status);
    case Reply:
      return acceptReply(incidence, status, method);
    case Refresh:
      return acceptRefresh(incidence, status);
    case Counter:
      return acceptCounter(incidence, status);
    default:
      break;
  }
  deleteTransaction(incidence);
  return false;
}

QString Scheduler::methodName(Method method)
{
  switch (method) {
    case Publish:
      return QString::fromLatin1("Publish");
    case Request:
      return QString::fromLatin1("Request");
    case Refresh:
      return QString::fromLatin1("Refresh");
    case Cancel:
      return QString::fromLatin1("Cancel");
    case Add:
      return QString::fromLatin1("Add");
    case Reply:
      return QString::fromLatin1("Reply");
    case Counter:
      return QString::fromLatin1("Counter");
    case Declinecounter:
      return QString::fromLatin1("Decline Counter");
    default:
      return QString::fromLatin1("Unknown");
  }
}

QString Scheduler::translatedMethodName(Method method)
{
  switch (method) {
    case Publish:
      return i18n("Publish");
    case Request:
      return i18n("Request");
    case Refresh:
      return i18n("Refresh");
    case Cancel:
      return i18n("Cancel");
    case Add:
      return i18n("Add");
    case Reply:
      return i18n("Reply");
    case Counter:
      return i18n("counter proposal","Counter");
    case Declinecounter:
      return i18n("decline counter proposal","Decline Counter");
    default:
      return i18n("Unknown");
  }
}

bool Scheduler::deleteTransaction(IncidenceBase *)
{
  return true;
}

bool Scheduler::acceptPublish( IncidenceBase *newIncBase,
                               ScheduleMessage::Status status, Method method )
{
  if( newIncBase->type() == "FreeBusy" ) {
    return acceptFreeBusy( newIncBase, method );
  }

  bool res = false;
  kdDebug(5800) << "Scheduler::acceptPublish, status="
            << ScheduleMessage::statusName( status ) << endl;
  Incidence *newInc = static_cast<Incidence *>( newIncBase );
  Incidence *calInc = mCalendar->incidence( newIncBase->uid() );
  switch ( status ) {
    case ScheduleMessage::Unknown:
    case ScheduleMessage::PublishNew:
    case ScheduleMessage::PublishUpdate:
      res = true;
      if ( calInc ) {
        if ( (newInc->revision() > calInc->revision()) ||
             (newInc->revision() == calInc->revision() &&
               newInc->lastModified() > calInc->lastModified() ) ) {
          mCalendar->deleteIncidence( calInc );
        } else
          res = false;
      }
      if ( res )
        mCalendar->addIncidence( newInc );
      break;
    case ScheduleMessage::Obsolete:
      res = true;
      break;
    default:
      break;
  }
  deleteTransaction( newIncBase );
  return res;
}

bool Scheduler::acceptRequest(IncidenceBase *newIncBase, ScheduleMessage::Status /* status */)
{
  if (newIncBase->type()=="FreeBusy") {
    // reply to this request is handled in korganizer's incomingdialog
    return true;
  }
  Incidence *newInc = dynamic_cast<Incidence *>( newIncBase );
  if ( newInc ) {
    bool res = true;
    Incidence *exInc = mCalendar->incidenceFromSchedulingID( newIncBase->uid() );
    if ( exInc ) {
      res = false;
      if ( (newInc->revision() > exInc->revision()) ||
           (newInc->revision() == exInc->revision() &&
             newInc->lastModified()>exInc->lastModified()) ) {
        mCalendar->deleteIncidence( exInc );
        res = true;
      }
    }
    if ( res ) {
      // Move the uid to be the schedulingID and make a unique UID
      newInc->setSchedulingID( newInc->uid() );
      newInc->setUid( CalFormat::createUniqueId() );

      mCalendar->addIncidence(newInc);
    }
    deleteTransaction( newIncBase );
    return res;
  }
  return false;
}

bool Scheduler::acceptAdd(IncidenceBase *incidence,ScheduleMessage::Status /* status */)
{
  deleteTransaction(incidence);
  return false;
}

bool Scheduler::acceptCancel(IncidenceBase *incidence,ScheduleMessage::Status /* status */)
{
  bool ret = false;
  const IncidenceBase *toDelete = mCalendar->incidenceFromSchedulingID( incidence->uid() );
  if ( toDelete ) {
    Event *even = mCalendar->event(toDelete->uid());
    if (even) {
      mCalendar->deleteEvent(even);
      ret = true;
    } else {
      Todo *todo = mCalendar->todo(toDelete->uid());
      if (todo) {
        mCalendar->deleteTodo(todo);
        ret = true;
      }
    }
  }
  deleteTransaction(incidence);
  return ret;
}

bool Scheduler::acceptDeclineCounter(IncidenceBase *incidence,ScheduleMessage::Status /* status */)
{
  deleteTransaction(incidence);
  return false;
}

//bool Scheduler::acceptFreeBusy(Incidence *incidence,ScheduleMessage::Status status)
//{
//  deleteTransaction(incidence);
//  return false;
//}

bool Scheduler::acceptReply(IncidenceBase *incidence,ScheduleMessage::Status /* status */, Method method)
{
  if(incidence->type()=="FreeBusy") {
    return acceptFreeBusy(incidence, method);
  }
  bool ret = false;
  Event *ev = mCalendar->event(incidence->uid());
  Todo *to = mCalendar->todo(incidence->uid());
  if (ev || to) {
    //get matching attendee in calendar
    kdDebug(5800) << "Scheduler::acceptTransaction match found!" << endl;
    Attendee::List attendeesIn = incidence->attendees();
    Attendee::List attendeesEv;
    if (ev) attendeesEv = ev->attendees();
    if (to) attendeesEv = to->attendees();
    Attendee::List::ConstIterator inIt;
    Attendee::List::ConstIterator evIt;
    for ( inIt = attendeesIn.begin(); inIt != attendeesIn.end(); ++inIt ) {
      Attendee *attIn = *inIt;
      for ( evIt = attendeesEv.begin(); evIt != attendeesEv.end(); ++evIt ) {
        Attendee *attEv = *evIt;
        if (attIn->email().lower()==attEv->email().lower()) {
          //update attendee-info
          kdDebug(5800) << "Scheduler::acceptTransaction update attendee" << endl;
          attEv->setStatus(attIn->status());
          ret = true;
        }
      }
    }
    if ( ret ) {
      // We set at least one of the attendees, so the incidence changed
      // Note: This should not result in a sequence number bump
      if ( ev )
        ev->updated();
      else if ( to )
        to->updated();
    }
    if ( to ) {
      // for VTODO a REPLY can be used to update the completion status of
      // a task. see RFC2446 3.4.3
      Todo *update = dynamic_cast<Todo*> ( incidence );
      Q_ASSERT( update );
      if ( update && ( to->percentComplete() != update->percentComplete() ) ) {
        to->setPercentComplete( update->percentComplete() );
        to->updated();
      }
    }
  } else
    kdError(5800) << "No incidence for scheduling\n";
  if (ret) deleteTransaction(incidence);
  return ret;
}

bool Scheduler::acceptRefresh(IncidenceBase *incidence,ScheduleMessage::Status /* status */)
{
  // handled in korganizer's IncomingDialog
  deleteTransaction(incidence);
  return false;
}

bool Scheduler::acceptCounter(IncidenceBase *incidence,ScheduleMessage::Status /* status */)
{
  deleteTransaction(incidence);
  return false;
}

bool Scheduler::acceptFreeBusy(IncidenceBase *incidence, Method method)
{
  if ( !d->mFreeBusyCache ) {
    kdError() << "KCal::Scheduler: no FreeBusyCache." << endl;
    return false;
  }

  FreeBusy *freebusy = static_cast<FreeBusy *>(incidence);

  kdDebug(5800) << "acceptFreeBusy:: freeBusyDirName: " << freeBusyDir() << endl;

  Person from;
  if(method == Scheduler::Publish) {
    from = freebusy->organizer();
  }
  if((method == Scheduler::Reply) && (freebusy->attendeeCount() == 1)) {
    Attendee *attendee = freebusy->attendees().first();
    from = attendee->email();
  }

  if ( !d->mFreeBusyCache->saveFreeBusy( freebusy, from ) ) return false;

  deleteTransaction(incidence);
  return true;
}
<|endoftext|>


<commit_before>#include <qpdf/QPDFParser.hh>

#include <qpdf/QPDF.hh>
#include <qpdf/QPDFObjectHandle.hh>
#include <qpdf/QPDF_Array.hh>
#include <qpdf/QTC.hh>
#include <qpdf/QUtil.hh>
#include <qpdf/SparseOHArray.hh>

namespace
{
    struct StackFrame
    {
        StackFrame(std::shared_ptr<InputSource> input) :
            offset(input->tell()),
            contents_string(""),
            contents_offset(-1)
        {
        }

        std::vector<QPDFObjectHandle> olist;
        qpdf_offset_t offset;
        std::string contents_string;
        qpdf_offset_t contents_offset;
    };
} // namespace

QPDFObjectHandle
QPDFParser::parse(bool& empty, bool content_stream)
{
    // This method must take care not to resolve any objects. Don't
    // check the type of any object without first ensuring that it is
    // a direct object. Otherwise, doing so may have the side effect
    // of reading the object and changing the file pointer. If you do
    // this, it will cause a logic error to be thrown from
    // QPDF::inParse().

    QPDF::ParseGuard pg(context);

    empty = false;

    QPDFObjectHandle object;
    bool set_offset = false;

    std::vector<StackFrame> stack;
    stack.push_back(StackFrame(input));
    std::vector<parser_state_e> state_stack;
    state_stack.push_back(st_top);
    qpdf_offset_t offset;
    bool done = false;
    int bad_count = 0;
    int good_count = 0;
    bool b_contents = false;

    while (!done) {
        bool bad = false;
        auto& frame = stack.back();
        auto& olist = frame.olist;
        parser_state_e state = state_stack.back();
        offset = frame.offset;

        object = QPDFObjectHandle();
        set_offset = false;

        QPDFTokenizer::Token token =
            tokenizer.readToken(input, object_description, true);
        std::string const& token_error_message = token.getErrorMessage();
        if (!token_error_message.empty()) {
            // Tokens other than tt_bad can still generate warnings.
            warn(token_error_message);
        }

        switch (token.getType()) {
        case QPDFTokenizer::tt_eof:
            if (!content_stream) {
                QTC::TC("qpdf", "QPDFParser eof in parse");
                warn("unexpected EOF");
            }
            bad = true;
            state = st_eof;
            break;

        case QPDFTokenizer::tt_bad:
            QTC::TC("qpdf", "QPDFParser bad token in parse");
            bad = true;
            object = QPDFObjectHandle::newNull();
            break;

        case QPDFTokenizer::tt_brace_open:
        case QPDFTokenizer::tt_brace_close:
            QTC::TC("qpdf", "QPDFParser bad brace");
            warn("treating unexpected brace token as null");
            bad = true;
            object = QPDFObjectHandle::newNull();
            break;

        case QPDFTokenizer::tt_array_close:
            if (state == st_array) {
                state = st_stop;
            } else {
                QTC::TC("qpdf", "QPDFParser bad array close");
                warn("treating unexpected array close token as null");
                bad = true;
                object = QPDFObjectHandle::newNull();
            }
            break;

        case QPDFTokenizer::tt_dict_close:
            if (state == st_dictionary) {
                state = st_stop;
            } else {
                QTC::TC("qpdf", "QPDFParser bad dictionary close");
                warn("unexpected dictionary close token");
                bad = true;
                object = QPDFObjectHandle::newNull();
            }
            break;

        case QPDFTokenizer::tt_array_open:
        case QPDFTokenizer::tt_dict_open:
            if (stack.size() > 500) {
                QTC::TC("qpdf", "QPDFParser too deep");
                warn("ignoring excessively deeply nested data structure");
                bad = true;
                object = QPDFObjectHandle::newNull();
                state = st_top;
            } else {
                state = st_start;
                state_stack.push_back(
                    (token.getType() == QPDFTokenizer::tt_array_open)
                        ? st_array
                        : st_dictionary);
                b_contents = false;
                stack.push_back(StackFrame(input));
            }
            break;

        case QPDFTokenizer::tt_bool:
            object = QPDFObjectHandle::newBool((token.getValue() == "true"));
            break;

        case QPDFTokenizer::tt_null:
            object = QPDFObjectHandle::newNull();
            break;

        case QPDFTokenizer::tt_integer:
            object = QPDFObjectHandle::newInteger(
                QUtil::string_to_ll(token.getValue().c_str()));
            break;

        case QPDFTokenizer::tt_real:
            object = QPDFObjectHandle::newReal(token.getValue());
            break;

        case QPDFTokenizer::tt_name:
            {
                std::string name = token.getValue();
                object = QPDFObjectHandle::newName(name);

                if (name == "/Contents") {
                    b_contents = true;
                } else {
                    b_contents = false;
                }
            }
            break;

        case QPDFTokenizer::tt_word:
            {
                std::string const& value = token.getValue();
                auto size = olist.size();
                if (content_stream) {
                    object = QPDFObjectHandle::newOperator(value);
                } else if (
                    (value == "R") && (state != st_top) && (size >= 2) &&
                    (!olist.back().isIndirect()) &&
                    (olist.back().isInteger()) &&
                    (!olist.at(size - 2).isIndirect()) &&
                    (olist.at(size - 2).isInteger())) {
                    if (context == nullptr) {
                        QTC::TC("qpdf", "QPDFParser indirect without context");
                        throw std::logic_error(
                            "QPDFObjectHandle::parse called without context"
                            " on an object with indirect references");
                    }
                    // Try to resolve indirect objects
                    object = QPDFObjectHandle::newIndirect(
                        context,
                        QPDFObjGen(
                            olist.at(size - 2).getIntValueAsInt(),
                            olist.back().getIntValueAsInt()));
                    olist.pop_back();
                    olist.pop_back();
                } else if ((value == "endobj") && (state == st_top)) {
                    // We just saw endobj without having read
                    // anything.  Treat this as a null and do not move
                    // the input source's offset.
                    object = QPDFObjectHandle::newNull();
                    input->seek(input->getLastOffset(), SEEK_SET);
                    empty = true;
                } else {
                    QTC::TC("qpdf", "QPDFParser treat word as string");
                    warn("unknown token while reading object;"
                         " treating as string");
                    bad = true;
                    object = QPDFObjectHandle::newString(value);
                }
            }
            break;

        case QPDFTokenizer::tt_string:
            {
                std::string val = token.getValue();
                if (decrypter) {
                    if (b_contents) {
                        frame.contents_string = val;
                        frame.contents_offset = input->getLastOffset();
                        b_contents = false;
                    }
                    decrypter->decryptString(val);
                }
                object = QPDFObjectHandle::newString(val);
            }

            break;

        default:
            warn("treating unknown token type as null while "
                 "reading object");
            bad = true;
            object = QPDFObjectHandle::newNull();
            break;
        }

        if ((!object.isInitialized()) &&
            (!((state == st_start) || (state == st_stop) ||
               (state == st_eof)))) {
            throw std::logic_error("QPDFObjectHandle::parseInternal: "
                                   "unexpected uninitialized object");
            object = QPDFObjectHandle::newNull();
        }

        if (bad) {
            ++bad_count;
            good_count = 0;
        } else {
            ++good_count;
            if (good_count > 3) {
                bad_count = 0;
            }
        }
        if (bad_count > 5) {
            // We had too many consecutive errors without enough
            // intervening successful objects. Give up.
            warn("too many errors; giving up on reading object");
            state = st_top;
            object = QPDFObjectHandle::newNull();
        }

        switch (state) {
        case st_eof:
            if (state_stack.size() > 1) {
                warn("parse error while reading object");
            }
            done = true;
            // In content stream mode, leave object uninitialized to
            // indicate EOF
            if (!content_stream) {
                object = QPDFObjectHandle::newNull();
            }
            break;

        case st_dictionary:
        case st_array:
            setDescriptionFromInput(object, input->getLastOffset());
            object.setParsedOffset(input->getLastOffset());
            set_offset = true;
            olist.push_back(object);
            break;

        case st_top:
            done = true;
            break;

        case st_start:
            break;

        case st_stop:
            if ((state_stack.size() < 2) || (stack.size() < 2)) {
                throw std::logic_error(
                    "QPDFObjectHandle::parseInternal: st_stop encountered"
                    " with insufficient elements in stack");
            }
            parser_state_e old_state = state_stack.back();
            state_stack.pop_back();
            if (old_state == st_array) {
                object = QPDFObjectHandle::newArray(olist);
                setDescriptionFromInput(object, offset);
                // The `offset` points to the next of "[".  Set the rewind
                // offset to point to the beginning of "[". This has been
                // explicitly tested with whitespace surrounding the array start
                // delimiter. getLastOffset points to the array end token and
                // therefore can't be used here.
                object.setParsedOffset(offset - 1);
                set_offset = true;
            } else if (old_state == st_dictionary) {
                // Convert list to map. Alternating elements are keys.  Attempt
                // to recover more or less gracefully from invalid dictionaries.
                std::set<std::string> names;
                size_t n_elements = olist.size();
                for (size_t i = 0; i < n_elements; ++i) {
                    QPDFObjectHandle oh = olist.at(i);
                    if ((!oh.isIndirect()) && oh.isName()) {
                        names.insert(oh.getName());
                    }
                }

                std::map<std::string, QPDFObjectHandle> dict;
                int next_fake_key = 1;
                for (unsigned int i = 0; i < n_elements; ++i) {
                    QPDFObjectHandle key_obj = olist.at(i);
                    QPDFObjectHandle val;
                    if (key_obj.isIndirect() || (!key_obj.isName())) {
                        bool found_fake = false;
                        std::string candidate;
                        while (!found_fake) {
                            candidate = "/QPDFFake" +
                                QUtil::int_to_string(next_fake_key++);
                            found_fake = (names.count(candidate) == 0);
                            QTC::TC(
                                "qpdf",
                                "QPDFParser found fake",
                                (found_fake ? 0 : 1));
                        }
                        warn(
                            offset,
                            "expected dictionary key but found"
                            " non-name object; inserting key " +
                                candidate);
                        val = key_obj;
                        key_obj = QPDFObjectHandle::newName(candidate);
                    } else if (i + 1 >= olist.size()) {
                        QTC::TC("qpdf", "QPDFParser no val for last key");
                        warn(
                            offset,
                            "dictionary ended prematurely; "
                            "using null as value for last key");
                        val = QPDFObjectHandle::newNull();
                        setDescriptionFromInput(val, offset);
                    } else {
                        val = olist.at(++i);
                    }
                    std::string key = key_obj.getName();
                    if (dict.count(key) > 0) {
                        QTC::TC("qpdf", "QPDFParser duplicate dict key");
                        warn(
                            offset,
                            "dictionary has duplicated key " + key +
                                "; last occurrence overrides earlier "
                                "ones");
                    }
                    dict[key] = val;
                }
                if (!frame.contents_string.empty() && dict.count("/Type") &&
                    dict["/Type"].isNameAndEquals("/Sig") &&
                    dict.count("/ByteRange") && dict.count("/Contents") &&
                    dict["/Contents"].isString()) {
                    dict["/Contents"] =
                        QPDFObjectHandle::newString(frame.contents_string);
                    dict["/Contents"].setParsedOffset(frame.contents_offset);
                }
                object = QPDFObjectHandle::newDictionary(dict);
                setDescriptionFromInput(object, offset);
                // The `offset` points to the next of "<<". Set the rewind
                // offset to point to the beginning of "<<". This has been
                // explicitly tested with whitespace surrounding the dictionary
                // start delimiter. getLastOffset points to the dictionary end
                // token and therefore can't be used here.
                object.setParsedOffset(offset - 2);
                set_offset = true;
            }
            stack.pop_back();
            if (state_stack.back() == st_top) {
                done = true;
            } else {
                stack.back().olist.push_back(object);
            }
        }
    }

    if (!set_offset) {
        setDescriptionFromInput(object, offset);
        object.setParsedOffset(offset);
    }
    return object;
}

void
QPDFParser::setDescriptionFromInput(
    QPDFObjectHandle oh, qpdf_offset_t offset) const
{
    oh.setObjectDescription(
        context,
        (input->getName() + ", " + object_description + " at offset " +
         QUtil::int_to_string(offset)));
}

void
QPDFParser::warn(QPDF* qpdf, QPDFExc const& e)
{
    // If parsing on behalf of a QPDF object and want to give a
    // warning, we can warn through the object. If parsing for some
    // other reason, such as an explicit creation of an object from a
    // string, then just throw the exception.
    if (qpdf) {
        qpdf->warn(e);
    } else {
        throw e;
    }
}

void
QPDFParser::warn(qpdf_offset_t offset, std::string const& msg) const
{
    warn(
        context,
        QPDFExc(
            qpdf_e_damaged_pdf,
            input->getName(),
            object_description,
            offset,
            msg));
}

void
QPDFParser::warn(std::string const& msg) const
{
    warn(input->getLastOffset(), msg);
}
<commit_msg>Avoid setting descriptions / offsets for direct nulls in QPDFParser::parse<commit_after>#include <qpdf/QPDFParser.hh>

#include <qpdf/QPDF.hh>
#include <qpdf/QPDFObjectHandle.hh>
#include <qpdf/QPDF_Array.hh>
#include <qpdf/QTC.hh>
#include <qpdf/QUtil.hh>
#include <qpdf/SparseOHArray.hh>

namespace
{
    struct StackFrame
    {
        StackFrame(std::shared_ptr<InputSource> input) :
            offset(input->tell()),
            contents_string(""),
            contents_offset(-1)
        {
        }

        std::vector<QPDFObjectHandle> olist;
        qpdf_offset_t offset;
        std::string contents_string;
        qpdf_offset_t contents_offset;
    };
} // namespace

QPDFObjectHandle
QPDFParser::parse(bool& empty, bool content_stream)
{
    // This method must take care not to resolve any objects. Don't
    // check the type of any object without first ensuring that it is
    // a direct object. Otherwise, doing so may have the side effect
    // of reading the object and changing the file pointer. If you do
    // this, it will cause a logic error to be thrown from
    // QPDF::inParse().

    QPDF::ParseGuard pg(context);

    empty = false;

    QPDFObjectHandle object;
    bool set_offset = false;

    std::vector<StackFrame> stack;
    stack.push_back(StackFrame(input));
    std::vector<parser_state_e> state_stack;
    state_stack.push_back(st_top);
    qpdf_offset_t offset;
    bool done = false;
    int bad_count = 0;
    int good_count = 0;
    bool b_contents = false;

    while (!done) {
        bool bad = false;
        auto& frame = stack.back();
        auto& olist = frame.olist;
        parser_state_e state = state_stack.back();
        offset = frame.offset;

        object = QPDFObjectHandle();
        set_offset = false;

        QPDFTokenizer::Token token =
            tokenizer.readToken(input, object_description, true);
        std::string const& token_error_message = token.getErrorMessage();
        if (!token_error_message.empty()) {
            // Tokens other than tt_bad can still generate warnings.
            warn(token_error_message);
        }

        switch (token.getType()) {
        case QPDFTokenizer::tt_eof:
            if (!content_stream) {
                QTC::TC("qpdf", "QPDFParser eof in parse");
                warn("unexpected EOF");
            }
            bad = true;
            state = st_eof;
            break;

        case QPDFTokenizer::tt_bad:
            QTC::TC("qpdf", "QPDFParser bad token in parse");
            bad = true;
            object = QPDFObjectHandle::newNull();
            break;

        case QPDFTokenizer::tt_brace_open:
        case QPDFTokenizer::tt_brace_close:
            QTC::TC("qpdf", "QPDFParser bad brace");
            warn("treating unexpected brace token as null");
            bad = true;
            object = QPDFObjectHandle::newNull();
            break;

        case QPDFTokenizer::tt_array_close:
            if (state == st_array) {
                state = st_stop;
            } else {
                QTC::TC("qpdf", "QPDFParser bad array close");
                warn("treating unexpected array close token as null");
                bad = true;
                object = QPDFObjectHandle::newNull();
            }
            break;

        case QPDFTokenizer::tt_dict_close:
            if (state == st_dictionary) {
                state = st_stop;
            } else {
                QTC::TC("qpdf", "QPDFParser bad dictionary close");
                warn("unexpected dictionary close token");
                bad = true;
                object = QPDFObjectHandle::newNull();
            }
            break;

        case QPDFTokenizer::tt_array_open:
        case QPDFTokenizer::tt_dict_open:
            if (stack.size() > 500) {
                QTC::TC("qpdf", "QPDFParser too deep");
                warn("ignoring excessively deeply nested data structure");
                bad = true;
                object = QPDFObjectHandle::newNull();
                state = st_top;
            } else {
                state = st_start;
                state_stack.push_back(
                    (token.getType() == QPDFTokenizer::tt_array_open)
                        ? st_array
                        : st_dictionary);
                b_contents = false;
                stack.push_back(StackFrame(input));
            }
            break;

        case QPDFTokenizer::tt_bool:
            object = QPDFObjectHandle::newBool((token.getValue() == "true"));
            break;

        case QPDFTokenizer::tt_null:
            object = QPDFObjectHandle::newNull();
            break;

        case QPDFTokenizer::tt_integer:
            object = QPDFObjectHandle::newInteger(
                QUtil::string_to_ll(token.getValue().c_str()));
            break;

        case QPDFTokenizer::tt_real:
            object = QPDFObjectHandle::newReal(token.getValue());
            break;

        case QPDFTokenizer::tt_name:
            {
                std::string name = token.getValue();
                object = QPDFObjectHandle::newName(name);

                if (name == "/Contents") {
                    b_contents = true;
                } else {
                    b_contents = false;
                }
            }
            break;

        case QPDFTokenizer::tt_word:
            {
                std::string const& value = token.getValue();
                auto size = olist.size();
                if (content_stream) {
                    object = QPDFObjectHandle::newOperator(value);
                } else if (
                    (value == "R") && (state != st_top) && (size >= 2) &&
                    (!olist.back().isIndirect()) &&
                    (olist.back().isInteger()) &&
                    (!olist.at(size - 2).isIndirect()) &&
                    (olist.at(size - 2).isInteger())) {
                    if (context == nullptr) {
                        QTC::TC("qpdf", "QPDFParser indirect without context");
                        throw std::logic_error(
                            "QPDFObjectHandle::parse called without context"
                            " on an object with indirect references");
                    }
                    // Try to resolve indirect objects
                    object = QPDFObjectHandle::newIndirect(
                        context,
                        QPDFObjGen(
                            olist.at(size - 2).getIntValueAsInt(),
                            olist.back().getIntValueAsInt()));
                    olist.pop_back();
                    olist.pop_back();
                } else if ((value == "endobj") && (state == st_top)) {
                    // We just saw endobj without having read
                    // anything.  Treat this as a null and do not move
                    // the input source's offset.
                    object = QPDFObjectHandle::newNull();
                    input->seek(input->getLastOffset(), SEEK_SET);
                    empty = true;
                } else {
                    QTC::TC("qpdf", "QPDFParser treat word as string");
                    warn("unknown token while reading object;"
                         " treating as string");
                    bad = true;
                    object = QPDFObjectHandle::newString(value);
                }
            }
            break;

        case QPDFTokenizer::tt_string:
            {
                std::string val = token.getValue();
                if (decrypter) {
                    if (b_contents) {
                        frame.contents_string = val;
                        frame.contents_offset = input->getLastOffset();
                        b_contents = false;
                    }
                    decrypter->decryptString(val);
                }
                object = QPDFObjectHandle::newString(val);
            }

            break;

        default:
            warn("treating unknown token type as null while "
                 "reading object");
            bad = true;
            object = QPDFObjectHandle::newNull();
            break;
        }

        if ((!object.isInitialized()) &&
            (!((state == st_start) || (state == st_stop) ||
               (state == st_eof)))) {
            throw std::logic_error("QPDFObjectHandle::parseInternal: "
                                   "unexpected uninitialized object");
            object = QPDFObjectHandle::newNull();
        }

        if (bad) {
            ++bad_count;
            good_count = 0;
        } else {
            ++good_count;
            if (good_count > 3) {
                bad_count = 0;
            }
        }
        if (bad_count > 5) {
            // We had too many consecutive errors without enough
            // intervening successful objects. Give up.
            warn("too many errors; giving up on reading object");
            state = st_top;
            object = QPDFObjectHandle::newNull();
        }

        switch (state) {
        case st_eof:
            if (state_stack.size() > 1) {
                warn("parse error while reading object");
            }
            done = true;
            // In content stream mode, leave object uninitialized to
            // indicate EOF
            if (!content_stream) {
                object = QPDFObjectHandle::newNull();
            }
            break;

        case st_dictionary:
        case st_array:
            if (!object.isDirectNull()) {
                // No need to set description for direct nulls- they will
                // become implicit.
                setDescriptionFromInput(object, input->getLastOffset());
                object.setParsedOffset(input->getLastOffset());
            }
            set_offset = true;
            olist.push_back(object);
            break;

        case st_top:
            done = true;
            break;

        case st_start:
            break;

        case st_stop:
            if ((state_stack.size() < 2) || (stack.size() < 2)) {
                throw std::logic_error(
                    "QPDFObjectHandle::parseInternal: st_stop encountered"
                    " with insufficient elements in stack");
            }
            parser_state_e old_state = state_stack.back();
            state_stack.pop_back();
            if (old_state == st_array) {
                object = QPDFObjectHandle::newArray(olist);
                setDescriptionFromInput(object, offset);
                // The `offset` points to the next of "[".  Set the rewind
                // offset to point to the beginning of "[". This has been
                // explicitly tested with whitespace surrounding the array start
                // delimiter. getLastOffset points to the array end token and
                // therefore can't be used here.
                object.setParsedOffset(offset - 1);
                set_offset = true;
            } else if (old_state == st_dictionary) {
                // Convert list to map. Alternating elements are keys.  Attempt
                // to recover more or less gracefully from invalid dictionaries.
                std::set<std::string> names;
                size_t n_elements = olist.size();
                for (size_t i = 0; i < n_elements; ++i) {
                    QPDFObjectHandle oh = olist.at(i);
                    if ((!oh.isIndirect()) && oh.isName()) {
                        names.insert(oh.getName());
                    }
                }

                std::map<std::string, QPDFObjectHandle> dict;
                int next_fake_key = 1;
                for (unsigned int i = 0; i < n_elements; ++i) {
                    QPDFObjectHandle key_obj = olist.at(i);
                    QPDFObjectHandle val;
                    if (key_obj.isIndirect() || (!key_obj.isName())) {
                        bool found_fake = false;
                        std::string candidate;
                        while (!found_fake) {
                            candidate = "/QPDFFake" +
                                QUtil::int_to_string(next_fake_key++);
                            found_fake = (names.count(candidate) == 0);
                            QTC::TC(
                                "qpdf",
                                "QPDFParser found fake",
                                (found_fake ? 0 : 1));
                        }
                        warn(
                            offset,
                            "expected dictionary key but found"
                            " non-name object; inserting key " +
                                candidate);
                        val = key_obj;
                        key_obj = QPDFObjectHandle::newName(candidate);
                    } else if (i + 1 >= olist.size()) {
                        QTC::TC("qpdf", "QPDFParser no val for last key");
                        warn(
                            offset,
                            "dictionary ended prematurely; "
                            "using null as value for last key");
                        val = QPDFObjectHandle::newNull();
                        setDescriptionFromInput(val, offset);
                    } else {
                        val = olist.at(++i);
                    }
                    std::string key = key_obj.getName();
                    if (dict.count(key) > 0) {
                        QTC::TC("qpdf", "QPDFParser duplicate dict key");
                        warn(
                            offset,
                            "dictionary has duplicated key " + key +
                                "; last occurrence overrides earlier "
                                "ones");
                    }
                    dict[key] = val;
                }
                if (!frame.contents_string.empty() && dict.count("/Type") &&
                    dict["/Type"].isNameAndEquals("/Sig") &&
                    dict.count("/ByteRange") && dict.count("/Contents") &&
                    dict["/Contents"].isString()) {
                    dict["/Contents"] =
                        QPDFObjectHandle::newString(frame.contents_string);
                    dict["/Contents"].setParsedOffset(frame.contents_offset);
                }
                object = QPDFObjectHandle::newDictionary(dict);
                setDescriptionFromInput(object, offset);
                // The `offset` points to the next of "<<". Set the rewind
                // offset to point to the beginning of "<<". This has been
                // explicitly tested with whitespace surrounding the dictionary
                // start delimiter. getLastOffset points to the dictionary end
                // token and therefore can't be used here.
                object.setParsedOffset(offset - 2);
                set_offset = true;
            }
            stack.pop_back();
            if (state_stack.back() == st_top) {
                done = true;
            } else {
                stack.back().olist.push_back(object);
            }
        }
    }

    if (!set_offset) {
        setDescriptionFromInput(object, offset);
        object.setParsedOffset(offset);
    }
    return object;
}

void
QPDFParser::setDescriptionFromInput(
    QPDFObjectHandle oh, qpdf_offset_t offset) const
{
    oh.setObjectDescription(
        context,
        (input->getName() + ", " + object_description + " at offset " +
         QUtil::int_to_string(offset)));
}

void
QPDFParser::warn(QPDF* qpdf, QPDFExc const& e)
{
    // If parsing on behalf of a QPDF object and want to give a
    // warning, we can warn through the object. If parsing for some
    // other reason, such as an explicit creation of an object from a
    // string, then just throw the exception.
    if (qpdf) {
        qpdf->warn(e);
    } else {
        throw e;
    }
}

void
QPDFParser::warn(qpdf_offset_t offset, std::string const& msg) const
{
    warn(
        context,
        QPDFExc(
            qpdf_e_damaged_pdf,
            input->getName(),
            object_description,
            offset,
            msg));
}

void
QPDFParser::warn(std::string const& msg) const
{
    warn(input->getLastOffset(), msg);
}
<|endoftext|>


<commit_before>#include <qpdf/QPDF_Array.hh>
#include <stdexcept>

QPDF_Array::QPDF_Array(std::vector<QPDFObjectHandle> const& items) :
    items(items)
{
}

QPDF_Array::~QPDF_Array()
{
}

void
QPDF_Array::releaseResolved()
{
    for (std::vector<QPDFObjectHandle>::iterator iter = this->items.begin();
	 iter != this->items.end(); ++iter)
    {
	QPDFObjectHandle::ReleaseResolver::releaseResolved(*iter);
    }
}

std::string
QPDF_Array::unparse()
{
    std::string result = "[ ";
    for (std::vector<QPDFObjectHandle>::iterator iter = this->items.begin();
	 iter != this->items.end(); ++iter)
    {
	result += (*iter).unparse();
	result += " ";
    }
    result += "]";
    return result;
}

int
QPDF_Array::getNItems() const
{
    return (int)this->items.size();
}

QPDFObjectHandle
QPDF_Array::getItem(int n) const
{
    if ((n < 0) || (n >= (int)this->items.size()))
    {
	throw std::logic_error(
	    "INTERNAL ERROR: bounds array accessing QPDF_Array element");
    }
    return this->items[n];
}

std::vector<QPDFObjectHandle> const&
QPDF_Array::getAsVector() const
{
    return this->items;
}

void
QPDF_Array::setItem(int n, QPDFObjectHandle const& oh)
{
    // Call getItem for bounds checking
    (void) getItem(n);
    this->items[n] = oh;
}
<commit_msg>Fix wording error in error message<commit_after>#include <qpdf/QPDF_Array.hh>
#include <stdexcept>

QPDF_Array::QPDF_Array(std::vector<QPDFObjectHandle> const& items) :
    items(items)
{
}

QPDF_Array::~QPDF_Array()
{
}

void
QPDF_Array::releaseResolved()
{
    for (std::vector<QPDFObjectHandle>::iterator iter = this->items.begin();
	 iter != this->items.end(); ++iter)
    {
	QPDFObjectHandle::ReleaseResolver::releaseResolved(*iter);
    }
}

std::string
QPDF_Array::unparse()
{
    std::string result = "[ ";
    for (std::vector<QPDFObjectHandle>::iterator iter = this->items.begin();
	 iter != this->items.end(); ++iter)
    {
	result += (*iter).unparse();
	result += " ";
    }
    result += "]";
    return result;
}

int
QPDF_Array::getNItems() const
{
    return (int)this->items.size();
}

QPDFObjectHandle
QPDF_Array::getItem(int n) const
{
    if ((n < 0) || (n >= (int)this->items.size()))
    {
	throw std::logic_error(
	    "INTERNAL ERROR: bounds error accessing QPDF_Array element");
    }
    return this->items[n];
}

std::vector<QPDFObjectHandle> const&
QPDF_Array::getAsVector() const
{
    return this->items;
}

void
QPDF_Array::setItem(int n, QPDFObjectHandle const& oh)
{
    // Call getItem for bounds checking
    (void) getItem(n);
    this->items[n] = oh;
}
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2016 deipi.com LLC and contributors. All rights reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to
 * deal in the Software without restriction, including without limitation the
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 * sell copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 * IN THE SOFTWARE.
 */

#include "utils.h"

bool write_file_contents(const std::string& filename, const std::string& contents) {
	std::ofstream of(filename.data(), std::ios::out | std::ios::binary);
	if (of.bad()) {
		return false;
	}
	of.write(contents.data(), contents.size());
	return true;
}

bool read_file_contents(const std::string& filename, std::string* contents) {
	std::ifstream in(filename.data(), std::ios::in | std::ios::binary);
	if (in.bad()) {
		return false;
	}

	in.seekg(0, std::ios::end);
	contents->resize(static_cast<size_t>(in.tellg()));
	in.seekg(0, std::ios::beg);
	in.read(&(*contents)[0], contents->size());
	in.close();
	return true;
}

DB_Test::DB_Test(const std::string& db_name, const std::vector<std::string>& docs, int flags, const std::string& ct_type)
	: name_database(db_name)
{
	// Delete database to create new db.
	delete_files(name_database);
	create_manager();

	endpoints.add(create_endpoint(name_database));

	db_handler.reset(endpoints, flags, HTTP_GET);

	// Index documents in the database.
	size_t i = 1;
	for (const auto& doc : docs) {
		std::string buffer;
		try {
			if (!read_file_contents(doc, &buffer)) {
				destroy();
				L_ERR(nullptr, "Can not read the file %s", doc.c_str());
			} else if (db_handler.index(std::to_string(i++), false, get_body(buffer, ct_type).second, true, ct_type).first == 0) {
				destroy();
				THROW(Error, "File %s can not index", doc.c_str());
			}
		} catch (const std::exception& e) {
			destroy();
			THROW(Error, "File %s can not index [%s]", doc.c_str(), e.what());
		}
	}
}

DB_Test::~DB_Test()
{
	destroy();
}

void
DB_Test::destroy()
{
	XapiandManager::manager.reset();
	delete_files(name_database);
}

void
DB_Test::create_manager()
{
	if (!XapiandManager::manager) {
		opts_t opts = {
			TEST_VERBOSITY, TEST_DETACH, TEST_CHERT, TEST_SOLO, TEST_STRICT, TEST_OPTIMAL, TEST_DATABASE,
			TEST_CLUSTER_NAME, TEST_NODE_NAME, XAPIAND_HTTP_SERVERPORT, XAPIAND_BINARY_SERVERPORT,
			XAPIAND_DISCOVERY_SERVERPORT, XAPIAND_RAFT_SERVERPORT, TEST_PIDFILE,
			TEST_LOGFILE, TEST_UID, TEST_GID, TEST_DISCOVERY_GROUP, TEST_RAFT_GROUP,
			TEST_NUM_SERVERS, TEST_DBPOOL_SIZE, TEST_NUM_REPLICATORS, TEST_THREADPOOL_SIZE,
			TEST_ENDPOINT_LIST_SIZE, TEST_NUM_COMMITERS, NUM_FSYNCHERS, TEST_MAX_CLIENTS,
			MAX_DATABASES, TEST_MAX_FILES, TEST_EV_FLAG
		};

		ev::default_loop default_loop(opts.ev_flags);
		XapiandManager::manager = Worker::make_shared<XapiandManager>(&default_loop, opts.ev_flags, opts);
	}
}

std::pair<std::string, MsgPack>
DB_Test::get_body(const std::string& body, const std::string& ct_type)
{
	MsgPack msgpack;
	rapidjson::Document rdoc;
	switch (xxh64::hash(ct_type)) {
		case xxh64::hash(FORM_URLENCODED_CONTENT_TYPE):
			try {
				json_load(rdoc, body);
				msgpack = MsgPack(rdoc);
			} catch (const std::exception&) {
				msgpack = MsgPack(body);
			}
			break;
		case xxh64::hash(JSON_CONTENT_TYPE):
			json_load(rdoc, body);
			msgpack = MsgPack(rdoc);
			break;
		case xxh64::hash(MSGPACK_CONTENT_TYPE):
		case xxh64::hash(X_MSGPACK_CONTENT_TYPE):
			msgpack = MsgPack::unserialise(body);
			break;
		default:
			msgpack = MsgPack(body);
			break;
	}

	return std::make_pair(ct_type, msgpack);
}
<commit_msg>Fixing bug in tests/utils.cc<commit_after>/*
 * Copyright (C) 2016 deipi.com LLC and contributors. All rights reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to
 * deal in the Software without restriction, including without limitation the
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 * sell copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 * IN THE SOFTWARE.
 */

#include "utils.h"

bool write_file_contents(const std::string& filename, const std::string& contents) {
	std::ofstream of(filename.data(), std::ios::out | std::ios::binary);
	if (of.bad()) {
		return false;
	}
	of.write(contents.data(), contents.size());
	return true;
}

bool read_file_contents(const std::string& filename, std::string* contents) {
	std::ifstream in(filename.data(), std::ios::in | std::ios::binary);
	if (in.bad()) {
		return false;
	}

	in.seekg(0, std::ios::end);
	contents->resize(static_cast<size_t>(in.tellg()));
	in.seekg(0, std::ios::beg);
	in.read(&(*contents)[0], contents->size());
	in.close();
	return true;
}

DB_Test::DB_Test(const std::string& db_name, const std::vector<std::string>& docs, int flags, const std::string& ct_type)
	: name_database(db_name)
{
	// Delete database to create new db.
	delete_files(name_database);
	create_manager();

	endpoints.add(create_endpoint(name_database));

	db_handler.reset(endpoints, flags, HTTP_GET);

	// Index documents in the database.
	size_t i = 1;
	for (const auto& doc : docs) {
		std::string buffer;
		try {
			if (!read_file_contents(doc, &buffer)) {
				destroy();
				L_ERR(nullptr, "Can not read the file %s", doc.c_str());
			} else if (db_handler.index(std::to_string(i++), false, get_body(buffer, ct_type).second, true, ct_type_t(ct_type)).first == 0) {
				destroy();
				THROW(Error, "File %s can not index", doc.c_str());
			}
		} catch (const std::exception& e) {
			destroy();
			THROW(Error, "File %s can not index [%s]", doc.c_str(), e.what());
		}
	}
}

DB_Test::~DB_Test()
{
	destroy();
}

void
DB_Test::destroy()
{
	XapiandManager::manager.reset();
	delete_files(name_database);
}

void
DB_Test::create_manager()
{
	if (!XapiandManager::manager) {
		opts_t opts = {
			TEST_VERBOSITY, TEST_DETACH, TEST_CHERT, TEST_SOLO, TEST_STRICT, TEST_OPTIMAL, TEST_DATABASE,
			TEST_CLUSTER_NAME, TEST_NODE_NAME, XAPIAND_HTTP_SERVERPORT, XAPIAND_BINARY_SERVERPORT,
			XAPIAND_DISCOVERY_SERVERPORT, XAPIAND_RAFT_SERVERPORT, TEST_PIDFILE,
			TEST_LOGFILE, TEST_UID, TEST_GID, TEST_DISCOVERY_GROUP, TEST_RAFT_GROUP,
			TEST_NUM_SERVERS, TEST_DBPOOL_SIZE, TEST_NUM_REPLICATORS, TEST_THREADPOOL_SIZE,
			TEST_ENDPOINT_LIST_SIZE, TEST_NUM_COMMITERS, NUM_FSYNCHERS, TEST_MAX_CLIENTS,
			MAX_DATABASES, TEST_MAX_FILES, TEST_EV_FLAG
		};

		ev::default_loop default_loop(opts.ev_flags);
		XapiandManager::manager = Worker::make_shared<XapiandManager>(&default_loop, opts.ev_flags, opts);
	}
}

std::pair<std::string, MsgPack>
DB_Test::get_body(const std::string& body, const std::string& ct_type)
{
	MsgPack msgpack;
	rapidjson::Document rdoc;
	switch (xxh64::hash(ct_type)) {
		case xxh64::hash(FORM_URLENCODED_CONTENT_TYPE):
			try {
				json_load(rdoc, body);
				msgpack = MsgPack(rdoc);
			} catch (const std::exception&) {
				msgpack = MsgPack(body);
			}
			break;
		case xxh64::hash(JSON_CONTENT_TYPE):
			json_load(rdoc, body);
			msgpack = MsgPack(rdoc);
			break;
		case xxh64::hash(MSGPACK_CONTENT_TYPE):
		case xxh64::hash(X_MSGPACK_CONTENT_TYPE):
			msgpack = MsgPack::unserialise(body);
			break;
		default:
			msgpack = MsgPack(body);
			break;
	}

	return std::make_pair(ct_type, msgpack);
}
<|endoftext|>


<commit_before><commit_msg>warning C4702: unreachable code<commit_after><|endoftext|>


<commit_before>//----------------------------------*-C++-*----------------------------------//
/*!
 * \file   cxx11/tstLoops.cc
 * \author Thomas M. Evans
 * \date   Wed May 07 22:49:15 2014
 * \brief  C++-11 Loop testing.
 * \note   Copyright (C) 2014 Oak Ridge National Laboratory, UT-Battelle, LLC.
 */
//---------------------------------------------------------------------------//

#include <vector>

#include "gtest/utils_gtest.hh"

//---------------------------------------------------------------------------//
// TESTS
//---------------------------------------------------------------------------//

TEST(Loops, begin_end)
{
    int x[] = {1, 2, 3, 4};

    int n = 1;
    for (auto i = std::begin(x); i != std::end(x); ++i)
    {
        EXPECT_EQ(n, *i);
        ++n;
    }
    EXPECT_EQ(5, n);
}

//---------------------------------------------------------------------------//

TEST(Loops, const_range)
{
    int x[] = {1, 2, 3, 4};
    std::vector<int> y(3);
    y[0] = 10; y[1] = 11; y[2] = 12;

    int n = 1;
    for (auto i : x)
    {
        EXPECT_EQ(n, i);
        ++n;
    }
    EXPECT_EQ(5, n);

    n = 10;
    for (auto i : y)
    {
        EXPECT_EQ(n, i);
        ++n;
    }
    EXPECT_EQ(13, n);
}

//---------------------------------------------------------------------------//

TEST(Loops, mutable_range)
{
    int x[] = {1, 2, 3, 4};
    std::vector<int> y(3);
    y[0] = 10; y[1] = 11; y[2] = 12;

    for (auto &i : x)
    {
        i += 10;
    }

    for (auto i : y)
    {
        i += 10;
    }

    EXPECT_EQ(11, x[0]);
    EXPECT_EQ(12, x[1]);
    EXPECT_EQ(13, x[2]);
    EXPECT_EQ(14, x[3]);

    EXPECT_EQ(10, y[0]);
    EXPECT_EQ(11, y[1]);
    EXPECT_EQ(12, y[2]);

    for (auto &i : y)
    {
        i += 10;
    }

    EXPECT_EQ(20, y[0]);
    EXPECT_EQ(21, y[1]);
    EXPECT_EQ(22, y[2]);

    for (int i = 0; i < 3; ++i)
    {
        y[i] += 1;
    }

    EXPECT_EQ(21, y[0]);
    EXPECT_EQ(22, y[1]);
    EXPECT_EQ(23, y[2]);
}

//---------------------------------------------------------------------------//
//                 end of tstLoops.cc
//---------------------------------------------------------------------------//
<commit_msg>Added vec-bool test.<commit_after>//----------------------------------*-C++-*----------------------------------//
/*!
 * \file   cxx11/tstLoops.cc
 * \author Thomas M. Evans
 * \date   Wed May 07 22:49:15 2014
 * \brief  C++-11 Loop testing.
 * \note   Copyright (C) 2014 Oak Ridge National Laboratory, UT-Battelle, LLC.
 */
//---------------------------------------------------------------------------//

#include <vector>

#include "gtest/utils_gtest.hh"

//---------------------------------------------------------------------------//
// TESTS
//---------------------------------------------------------------------------//

TEST(Loops, begin_end)
{
    int x[] = {1, 2, 3, 4};

    int n = 1;
    for (auto i = std::begin(x); i != std::end(x); ++i)
    {
        EXPECT_EQ(n, *i);
        ++n;
    }
    EXPECT_EQ(5, n);
}

//---------------------------------------------------------------------------//

TEST(Loops, const_range)
{
    int x[] = {1, 2, 3, 4};
    std::vector<int> y(3);
    y[0] = 10; y[1] = 11; y[2] = 12;

    int n = 1;
    for (auto i : x)
    {
        EXPECT_EQ(n, i);
        ++n;
    }
    EXPECT_EQ(5, n);

    n = 10;
    for (auto i : y)
    {
        EXPECT_EQ(n, i);
        ++n;
    }
    EXPECT_EQ(13, n);
}

//---------------------------------------------------------------------------//

TEST(Loops, mutable_range)
{
    int x[] = {1, 2, 3, 4};
    std::vector<int> y(3);
    y[0] = 10; y[1] = 11; y[2] = 12;

    for (auto &i : x)
    {
        i += 10;
    }

    for (auto i : y)
    {
        i += 10;
    }

    EXPECT_EQ(11, x[0]);
    EXPECT_EQ(12, x[1]);
    EXPECT_EQ(13, x[2]);
    EXPECT_EQ(14, x[3]);

    EXPECT_EQ(10, y[0]);
    EXPECT_EQ(11, y[1]);
    EXPECT_EQ(12, y[2]);

    for (auto &i : y)
    {
        i += 10;
    }

    EXPECT_EQ(20, y[0]);
    EXPECT_EQ(21, y[1]);
    EXPECT_EQ(22, y[2]);

    for (int i = 0; i < 3; ++i)
    {
        y[i] += 1;
    }

    EXPECT_EQ(21, y[0]);
    EXPECT_EQ(22, y[1]);
    EXPECT_EQ(23, y[2]);
}

//---------------------------------------------------------------------------//

TEST(Loops, vector_bool)
{
    // vector<bool>'s are special because they are packed, thus they return a
    // *proxy* iterator

    bool v[] = {true, false, false, true};

    std::vector<bool> vb(std::begin(v), std::end(v));
    EXPECT_TRUE(vb[0]);
    EXPECT_FALSE(vb[1]);
    EXPECT_FALSE(vb[2]);
    EXPECT_TRUE(vb[3]);

    vb[1] = true;
    vb[2] = true;

    // only changes local value
    for (auto &&b : vb)
    {
        EXPECT_TRUE(b);
        b = false;
    }

    EXPECT_FALSE(vb[0]);
    EXPECT_FALSE(vb[1]);
    EXPECT_FALSE(vb[2]);
    EXPECT_FALSE(vb[3]);
}

//---------------------------------------------------------------------------//
//                 end of tstLoops.cc
//---------------------------------------------------------------------------//
<|endoftext|>