summaryrefslogtreecommitdiff
path: root/library/backend
Side-by-side diff
Diffstat (limited to 'library/backend') (more/less context) (ignore whitespace changes)
-rw-r--r--library/backend/.cvsignore2
-rw-r--r--library/backend/categories.cpp701
-rw-r--r--library/backend/categories.h232
-rw-r--r--library/backend/contact.cpp909
-rw-r--r--library/backend/contact.h217
-rw-r--r--library/backend/event.cpp830
-rw-r--r--library/backend/event.h229
-rw-r--r--library/backend/palmtoprecord.cpp127
-rw-r--r--library/backend/palmtoprecord.h94
-rw-r--r--library/backend/palmtopuidgen.h83
-rw-r--r--library/backend/qfiledirect_p.h36
-rw-r--r--library/backend/qpcglobal.h50
-rw-r--r--library/backend/recordfields.h135
-rw-r--r--library/backend/stringutil.cpp415
-rw-r--r--library/backend/stringutil.h57
-rw-r--r--library/backend/task.cpp271
-rw-r--r--library/backend/task.h77
-rw-r--r--library/backend/timeconversion.cpp237
-rw-r--r--library/backend/timeconversion.h45
-rw-r--r--library/backend/vcc.y1199
-rw-r--r--library/backend/vobject.cpp1210
-rw-r--r--library/backend/vobject_p.h401
22 files changed, 7557 insertions, 0 deletions
diff --git a/library/backend/.cvsignore b/library/backend/.cvsignore
new file mode 100644
index 0000000..e047b1f
--- a/dev/null
+++ b/library/backend/.cvsignore
@@ -0,0 +1,2 @@
+moc_*
+*.moc
diff --git a/library/backend/categories.cpp b/library/backend/categories.cpp
new file mode 100644
index 0000000..91331db
--- a/dev/null
+++ b/library/backend/categories.cpp
@@ -0,0 +1,701 @@
+/**********************************************************************
+** Copyright (C) 2000 Trolltech AS. All rights reserved.
+**
+** This file is part of Qtopia Environment.
+**
+** This file may be distributed and/or modified under the terms of the
+** GNU General Public License version 2 as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included
+** in the packaging of this file.
+**
+** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING
+** THE WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A
+** PARTICULAR PURPOSE.
+**
+** See http://www.trolltech.com/gpl/ for GPL licensing information.
+**
+** Contact info@trolltech.com if any conditions of this licensing are
+** not clear to you.
+**
+**********************************************************************/
+#include "categories.h"
+#include <qfile.h>
+#include <qcstring.h>
+#include <qtextstream.h>
+#include "stringutil.h"
+
+using namespace Qtopia;
+
+/***********************************************************
+ *
+ * CategoryGroup
+ *
+ **********************************************************/
+
+#ifdef PALMTOPCENTER
+UidGen CategoryGroup::sUidGen( UidGen::PalmtopCenter );
+#else
+UidGen CategoryGroup::sUidGen( UidGen::Qtopia );
+#endif
+
+int CategoryGroup::add( const QString &label )
+{
+ if ( label == QObject::tr("All") || label == QObject::tr("Unfiled") )
+ return 0;
+
+ QMap<QString,int>::Iterator findIt = mLabelIdMap.find( label );
+ if ( findIt != mLabelIdMap.end() )
+ return 0;
+ int newUid = uidGen().generate();
+ insert( newUid, label );
+ return newUid;
+}
+
+void CategoryGroup::insert( int uid, const QString &label )
+{
+ uidGen().store( uid );
+ mIdLabelMap[uid] = label;
+ mLabelIdMap[label] = uid;
+}
+
+bool CategoryGroup::add( int uid, const QString &label )
+{
+ if ( label == QObject::tr("All") || label == QObject::tr("Unfiled") )
+ return FALSE;
+
+ QMap<QString,int>::ConstIterator labelIt = mLabelIdMap.find( label );
+ if ( labelIt != mLabelIdMap.end() )
+ return FALSE;
+ QMap<int,QString>::ConstIterator idIt = mIdLabelMap.find( uid );
+ if ( idIt != mIdLabelMap.end() )
+ return FALSE;
+ insert( uid, label );
+ return TRUE;
+}
+
+bool CategoryGroup::remove( const QString &label )
+{
+ QMap<QString,int>::Iterator findIt = mLabelIdMap.find( label );
+ if ( findIt == mLabelIdMap.end() )
+ return FALSE;
+
+ mIdLabelMap.remove( *findIt );
+ mLabelIdMap.remove( findIt );
+
+ return TRUE;
+}
+
+bool CategoryGroup::remove( int uid )
+{
+ QMap<int,QString>::Iterator idIt = mIdLabelMap.find( uid );
+ if ( idIt == mIdLabelMap.end() )
+ return FALSE;
+
+ mLabelIdMap.remove( *idIt );
+ mIdLabelMap.remove( idIt );
+
+ return TRUE;
+}
+
+bool CategoryGroup::rename( int uid, const QString &newLabel )
+{
+ if ( newLabel == QObject::tr("All") || newLabel == QObject::tr("Unfiled") )
+ return FALSE;
+
+ QMap<int, QString>::Iterator idIt = mIdLabelMap.find( uid );
+ if ( idIt == mIdLabelMap.end() )
+ return FALSE;
+
+ mLabelIdMap.remove( *idIt );
+ mLabelIdMap[newLabel] = uid;
+ *idIt = newLabel;
+
+ return TRUE;
+}
+
+bool CategoryGroup::rename( const QString &oldLabel, const QString &newLabel )
+{
+ return rename( id(oldLabel), newLabel );
+}
+
+bool CategoryGroup::contains(int uid) const
+{
+ return ( mIdLabelMap.find( uid ) != mIdLabelMap.end() );
+}
+
+bool CategoryGroup::contains(const QString &label) const
+{
+ return ( mLabelIdMap.find( label ) != mLabelIdMap.end() );
+}
+
+/** Returns label associated with the uid or QString::null if
+ * not found
+ */
+const QString &CategoryGroup::label(int uid) const
+{
+ QMap<int,QString>::ConstIterator idIt = mIdLabelMap.find( uid );
+ if ( idIt == mIdLabelMap.end() )
+ return QString::null;
+ return *idIt;
+}
+
+/** Returns the uid associated with label or 0 if not found */
+int CategoryGroup::id(const QString &label) const
+{
+ QMap<QString,int>::ConstIterator labelIt = mLabelIdMap.find( label );
+ if ( labelIt == mLabelIdMap.end() )
+ return 0;
+ return *labelIt;
+}
+
+QStringList CategoryGroup::labels() const
+{
+ QStringList labels;
+ for ( QMap<int, QString>::ConstIterator it = mIdLabelMap.begin();
+ it != mIdLabelMap.end(); ++it )
+ labels += *it;
+ // ### I don't think this is the place for this...
+// labels.sort();
+ return labels;
+}
+
+QStringList CategoryGroup::labels(const QArray<int> &catids ) const
+{
+ QStringList labels;
+ if ( catids.count() == 0 )
+ return labels;
+ for ( QMap<int, QString>::ConstIterator it = mIdLabelMap.begin();
+ it != mIdLabelMap.end(); ++it )
+ if ( catids.find( it.key() ) != -1 )
+ labels += *it;
+ return labels;
+}
+
+QArray<int> CategoryGroup::ids( const QStringList &cats ) const
+{
+ QArray<int> results;
+
+ for ( QStringList::ConstIterator catIt = cats.begin();
+ catIt != cats.end(); ++catIt ) {
+ if ( *catIt == QObject::tr("All") || *catIt == QObject::tr("Unfiled") )
+ continue;
+ int value = id( *catIt );
+ if ( value != 0 ) {
+ int tmp = results.size();
+ results.resize( tmp + 1 );
+ results[ tmp ] = value;
+ }
+ }
+
+ return results;
+}
+
+QArray<int> CategoryGroup::ids() const
+{
+ QArray<int> results( mIdLabelMap.count() );
+ int i = 0;
+ for ( QMap<int, QString>::ConstIterator it = mIdLabelMap.begin();
+ it != mIdLabelMap.end(); ++it )
+ results[i++] = it.key();
+
+ return results;
+}
+
+/***********************************************************
+ *
+ * Categories
+ *
+ **********************************************************/
+
+/** Add the category name as long as it doesn't already exist locally
+ * or globally. Return TRUE if added, FALSE if conflicts.
+ */
+int Categories::addCategory( const QString &appname,
+ const QString &catname,
+ int uid )
+{
+ if ( mGlobalCats.contains(catname) )
+ return 0;
+
+ QMap< QString, CategoryGroup >::Iterator
+ appIt = mAppCats.find( appname );
+
+ if ( appIt == mAppCats.end() ) {
+ CategoryGroup newgroup;
+ newgroup.add( uid, catname );
+ mAppCats.insert( appname, newgroup );
+ emit categoryAdded( *this, appname, uid );
+ return uid;
+ }
+
+ CategoryGroup &cats = *appIt;
+ cats.add( uid, catname );
+ emit categoryAdded( *this, appname, uid );
+ return uid;
+}
+
+int Categories::addCategory( const QString &appname,
+ const QString &catname )
+{
+ if ( mGlobalCats.contains(catname) )
+ return 0;
+
+ QMap< QString, CategoryGroup >::Iterator
+ appIt = mAppCats.find( appname );
+
+ if ( appIt == mAppCats.end() ) {
+ CategoryGroup newgroup;
+ int uid = newgroup.add( catname );
+ mAppCats.insert( appname, newgroup );
+ emit categoryAdded( *this, appname, uid );
+ return uid;
+ }
+
+ CategoryGroup &cats = *appIt;
+ int uid = cats.add( catname );
+ if ( !uid )
+ return 0;
+ emit categoryAdded( *this, appname, uid );
+ return uid;
+}
+
+int Categories::addGlobalCategory( const QString &catname, int uid )
+{
+ mGlobalCats.add( uid, catname );
+ emit categoryAdded( *this, QString::null, uid );
+ return uid;
+}
+
+int Categories::addGlobalCategory( const QString &catname )
+{
+ int uid = mGlobalCats.add( catname );
+ if ( !uid )
+ return 0;
+ emit categoryAdded( *this, QString::null, uid );
+ return uid;
+}
+
+/** Removes the category from the application; if it is not found
+ * in the application, then it attempts to remove it from
+ * the global list
+ */
+bool Categories::removeCategory( const QString &appname,
+ const QString &catname,
+ bool checkGlobal )
+{
+ QMap< QString, CategoryGroup >::Iterator
+ appIt = mAppCats.find( appname );
+ if ( appIt != mAppCats.end() ) {
+ CategoryGroup &cats = *appIt;
+ int uid = cats.id( catname );
+ if ( cats.remove( uid ) ) {
+ emit categoryRemoved( *this, appname, uid );
+ return TRUE;
+ }
+ }
+ if ( !checkGlobal )
+ return FALSE;
+ return removeGlobalCategory( catname );
+}
+
+bool Categories::removeCategory( const QString &appname, int uid )
+{
+ QMap< QString, CategoryGroup >::Iterator
+ appIt = mAppCats.find( appname );
+ if ( appIt != mAppCats.end() ) {
+ CategoryGroup &cats = *appIt;
+ if ( cats.remove( uid ) ) {
+ emit categoryRemoved( *this, appname, uid );
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+bool Categories::removeGlobalCategory( const QString &catname )
+{
+ int uid = mGlobalCats.id( catname );
+ if ( mGlobalCats.remove( uid ) ) {
+ emit categoryRemoved( *this, QString::null, uid );
+ return TRUE;
+ }
+ return FALSE;
+}
+
+
+bool Categories::removeGlobalCategory( int uid )
+{
+ if ( mGlobalCats.remove( uid ) ) {
+ emit categoryRemoved( *this, QString::null, uid );
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/** Returns the sorted list of all categories that are associated with
+ * the app. If includeGlobal parameter is TRUE then the returned
+ * categories will include the global category items.
+ */
+QStringList Categories::labels( const QString &app,
+ bool includeGlobal,
+ ExtraLabels extra ) const
+{
+ QMap< QString, CategoryGroup >::ConstIterator
+ appIt = mAppCats.find( app );
+ QStringList cats;
+ switch ( extra ) {
+ case NoExtra: break;
+ case AllUnfiled:
+ cats.append( tr("All") );
+ cats.append( tr("Unfiled") );
+ break;
+ case AllLabel:
+ cats.append( tr("All") );
+ break;
+ case UnfiledLabel:
+ cats.append( tr("Unfiled") );
+ break;
+ }
+ if ( appIt != mAppCats.end() )
+ cats += (*appIt).labels();
+ else qDebug("Categories::labels didn't find app %s", app.latin1() );
+ if ( includeGlobal )
+ cats += mGlobalCats.labels();
+ // I don't think a sorted list is useful, the user might find prefer
+ // it in the original order.
+// cats.sort();
+ return cats;
+}
+
+QString Categories::label( const QString &app, int id ) const
+{
+ if ( mGlobalCats.contains( id ) )
+ return mGlobalCats.label( id );
+ QMap< QString, CategoryGroup >::ConstIterator
+ appIt = mAppCats.find( app );
+ if ( appIt == mAppCats.end() )
+ return QString::null;
+ return (*appIt).label( id );
+}
+
+QStringList Categories::labels( const QString & app,
+ const QArray<int> &catids ) const
+{
+ QStringList strs = mGlobalCats.labels( catids );
+ strs += mAppCats[app].labels( catids );
+ return strs;
+}
+
+/** Returns a single string associated with the cat ids for display in
+ * a combobox or any area that requires one string. If catids are empty
+ * then "Unfiled" will be returned. If multiple categories are assigned
+ * the first cat id is shown with " (multi)" appended to the string.
+ */
+QString Categories::displaySingle( const QString &app,
+ const QArray<int> &catids,
+ DisplaySingle display ) const
+{
+ QStringList strs = labels( app, catids );
+ if ( !strs.count() )
+ return tr("Unfiled");
+ strs.sort();
+ QString r;
+ if ( strs.count() > 1 ) {
+ switch ( display ) {
+ case ShowFirst:
+ r = strs.first();
+ break;
+ case ShowMulti:
+ r = strs.first() + tr(" (multi.)");
+ break;
+ case ShowAll:
+ r = strs.join(" ");
+ break;
+ }
+ }
+ else r = strs.first();
+ return r;
+}
+
+QArray<int> Categories::ids( const QString &app ) const
+{
+ QArray<int> allIds = mGlobalCats.ids();
+ QArray<int> appIds = mAppCats[app].ids();
+
+ // we should make the guarentee that the ids are in the
+ // same order as the labels, (i.e. app cats then global)
+ // otherwise there is no point in having these two separate functions.
+ uint appSize = appIds.size();
+ appIds.resize( appSize + allIds.size() );
+ for ( uint i = appSize; i < appIds.size(); ++i )
+ appIds[int(i)] = allIds[int(i - appSize)];
+
+ return appIds;
+}
+
+QArray<int> Categories::ids( const QString &app, const QStringList &cats ) const
+{
+ QArray<int> allIds = mGlobalCats.ids( cats );
+ QArray<int> appIds = mAppCats[app].ids( cats );
+
+ uint appSize = appIds.size();
+ appIds.resize( appSize + allIds.size() );
+ for ( uint i = appSize; i < appIds.size(); ++i )
+ appIds[int(i)] = allIds[int(i - appSize)];
+
+ return appIds;
+}
+
+int Categories::id( const QString &app, const QString &cat ) const
+{
+ if ( cat == tr("Unfiled") || cat.contains( tr(" (multi.)") ) )
+ return 0;
+ int uid = mGlobalCats.id( cat );
+ if ( uid != 0 )
+ return uid;
+ return mAppCats[app].id( cat );
+}
+
+
+/** Return TRUE if renaming succeeded; FALSE if app name not found,
+ * or if there was a name conflict
+ */
+bool Categories::renameCategory( const QString &appname,
+ const QString &oldName,
+ const QString &newName )
+{
+ QMap< QString, CategoryGroup >::Iterator
+ appIt = mAppCats.find( appname );
+
+ if ( appIt != mAppCats.end() ) {
+ CategoryGroup &cats = *appIt;
+ int id = cats.id( oldName );
+ if ( id != 0 && cats.rename( id, newName ) ) {
+ emit categoryRenamed( *this, appname, id );
+ return TRUE;
+ }
+ }
+ return renameGlobalCategory( oldName, newName );
+}
+
+bool Categories::renameGlobalCategory( const QString &oldName,
+ const QString &newName )
+{
+ int uid = mGlobalCats.id( oldName );
+ if ( uid != 0 && mGlobalCats.rename( uid, newName ) ) {
+ emit categoryRenamed( *this, QString::null, uid );
+ return TRUE;
+ }
+ return FALSE;
+}
+
+void Categories::setGlobal( const QString &appname,
+ const QString &catname,
+ bool global )
+{
+ // if in global and should be in app; then move it
+ if ( mGlobalCats.contains( catname ) && !global ) {
+ mGlobalCats.remove( catname );
+ addCategory( appname, catname );
+ return ;
+ }
+
+ // if in app and should be in global, then move it
+ if ( !global )
+ return;
+ if ( removeCategory( appname, catname, FALSE ) )
+ addGlobalCategory( catname );
+}
+
+bool Categories::isGlobal( const QString &catname ) const
+{
+ return mGlobalCats.contains( catname );
+}
+
+
+/** Returns true if the catname is associated with any application
+ */
+bool Categories::exists( const QString &catname ) const
+{
+ if ( isGlobal(catname) )
+ return TRUE;
+
+ for ( QMap<QString, CategoryGroup>::ConstIterator appsIt = mAppCats.begin(); appsIt != mAppCats.end(); ++appsIt )
+ if ( exists( appsIt.key(), catname ) )
+ return TRUE;
+
+ return FALSE;
+}
+
+bool Categories::exists( const QString &appname,
+ const QString &catname) const
+{
+ QMap< QString, CategoryGroup >::ConstIterator
+ appIt = mAppCats.find( appname );
+
+ if ( appIt == mAppCats.end() )
+ return FALSE;
+
+ return (*appIt).contains( catname );
+}
+
+bool Categories::save( const QString &fname ) const
+{
+ QFile file( fname );
+ if ( !file.open( IO_WriteOnly ) ) {
+ qWarning("Unable to write to %s", fname.latin1());
+ return FALSE;
+ }
+
+ QTextStream ts( &file );
+ ts << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
+ ts << "<!DOCTYPE CategoryList>" << endl;
+
+ ts << "<Categories>" << endl;
+ for ( QMap<int, QString>::ConstIterator git = mGlobalCats.idMap().begin();
+ git != mGlobalCats.idMap().end(); ++git )
+ ts << "<Category id=\"" << git.key() << "\""
+ << " name=\"" << escapeString(*git) << "\" />" << endl;
+
+ for ( QMap<QString, CategoryGroup>::ConstIterator appsIt=mAppCats.begin();
+ appsIt != mAppCats.end(); ++appsIt ) {
+ const QString &app = appsIt.key();
+ const QMap<int, QString> &appcats = (*appsIt).idMap();
+ for ( QMap<int, QString>::ConstIterator appcatit = appcats.begin();
+ appcatit != appcats.end(); ++appcatit )
+ ts << "<Category id=\"" << appcatit.key() << "\""
+ << " app=\"" << escapeString(app) << "\""
+ << " name=\"" << escapeString(*appcatit) << "\" />" << endl;
+ }
+ ts << "</Categories>" << endl;
+
+ file.close();
+ return TRUE;
+}
+
+bool Categories::load( const QString &fname )
+{
+ QFile file( fname );
+ if ( !file.open( IO_ReadOnly ) ) {
+ qWarning("Unable to open %s", fname.latin1());
+ return FALSE;
+ }
+
+ clear();
+ QByteArray ba = file.readAll();
+ QString data = QString::fromUtf8( ba.data(), ba.size() );
+ QChar *uc = (QChar *)data.unicode();
+ int len = data.length();
+
+ // QTime t;
+ // t.start();
+ QString name;
+ QString id;
+ QString app;
+ int i = 0;
+ while ( (i = data.find( "<Category ", i)) != -1 ) {
+
+ i += 10;
+ name = QString::null;
+ app = QString::null;
+ while ( 1 ) {
+ // skip white space
+ while ( i < len &&
+ (uc[i] == ' ' || uc[i] == '\n' || uc[i] == '\r') )
+ i++;
+ // if at the end, then done
+ if ( i >= len-2 || (uc[i] == '/' && uc[i+1] == '>') )
+ break;
+ // we have another attribute read it.
+ int j = i;
+ while ( j < len && uc[j] != '=' )
+ j++;
+ QString attr = QConstString( uc+i, j-i ).string();
+ i = ++j; // skip =
+ while ( i < len && uc[i] != '"' )
+ i++;
+ j = ++i;
+ while ( j < len && uc[j] != '"' )
+ j++;
+ QString value = Qtopia::plainString( QConstString( uc+i, j-i ).string() );
+ i = j + 1;
+
+// qDebug("attr='%s' value='%s'", attr.latin1(), value.latin1() );
+ if ( attr == "id" )
+ id = value;
+ else if ( attr == "app" )
+ app = value;
+
+ else if ( attr == "name" )
+ name = value;
+ }
+
+ if ( name.isNull() || id.isNull() ) {
+ qWarning("No name or id in the category");
+ continue;
+ }
+ if ( app.isNull() )
+ mGlobalCats.add( id.toInt(), name );
+ else
+ mAppCats[ app ].add( id.toInt(), name );
+ }
+
+ return TRUE;
+}
+
+void Categories::clear()
+{
+ mGlobalCats.clear();
+ mAppCats.clear();
+}
+
+void Categories::dump() const
+{
+ qDebug("\tglobal categories = %s", mGlobalCats.labels().join(", ").latin1() );
+ for ( QMap<QString, CategoryGroup>::ConstIterator appsIt = mAppCats.begin(); appsIt != mAppCats.end(); ++appsIt ) {
+ const QString &app = appsIt.key();
+ QStringList appcats = (*appsIt).labels();
+ qDebug("\tapp = %s\tcategories = %s", app.latin1(),
+ appcats.join(", ").latin1() );
+
+ }
+}
+
+QStringList CheckedListView::checked() const
+{
+ QStringList strs;
+ for ( QCheckListItem *i = (QCheckListItem *) firstChild();
+ i; i = (QCheckListItem *)i->nextSibling() )
+ if ( i->isOn() )
+ strs += i->text( 0 );
+ return strs;
+}
+
+void CheckedListView::addCheckableList( const QStringList &options )
+{
+ for ( QStringList::ConstIterator it = options.begin();
+ it != options.end(); ++it ) {
+ (void) new QCheckListItem( this, *it,
+ QCheckListItem::CheckBox );
+ }
+}
+
+void CheckedListView::setChecked( const QStringList &checked )
+{
+ // iterate over all items
+ bool showingChecked = FALSE;
+ for ( QCheckListItem *i = (QCheckListItem *) firstChild();
+ i; i = (QCheckListItem *)i->nextSibling() )
+ // see if the item should be checked by searching the
+ // checked list
+ if ( checked.find( i->text( 0 ) ) != checked.end() ) {
+ i->setOn( TRUE );
+ // make sure it is showing at least one checked item
+ if ( !showingChecked ) {
+ ensureItemVisible( i );
+ showingChecked = TRUE;
+ }
+ }
+ else
+ i->setOn( FALSE );
+}
diff --git a/library/backend/categories.h b/library/backend/categories.h
new file mode 100644
index 0000000..82d765b
--- a/dev/null
+++ b/library/backend/categories.h
@@ -0,0 +1,232 @@
+/**********************************************************************
+** Copyright (C) 2000 Trolltech AS. All rights reserved.
+**
+** This file is part of Qtopia Environment.
+**
+** This file may be distributed and/or modified under the terms of the
+** GNU General Public License version 2 as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included
+** in the packaging of this file.
+**
+** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING
+** THE WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A
+** PARTICULAR PURPOSE.
+**
+** See http://www.trolltech.com/gpl/ for GPL licensing information.
+**
+** Contact info@trolltech.com if any conditions of this licensing are
+** not clear to you.
+**
+**********************************************************************/
+
+#ifndef QTPALMTOP_CATEGORIES_H
+#define QTPALMTOP_CATEGORIES_H
+
+#include <qstring.h>
+#include <qstringlist.h>
+#include <qmap.h>
+#include <qlistview.h>
+#include <qarray.h>
+#include "qpcglobal.h"
+#include "palmtopuidgen.h"
+
+class CategoryGroup;
+
+#if defined(QPC_TEMPLATEDLL)
+// MOC_SKIP_BEGIN
+template class QPC_EXPORT QMap<int, QString>;
+template class QPC_EXPORT QMap<QString, int>;
+template class QPC_EXPORT QMap< QString, CategoryGroup >;
+// MOC_SKIP_END
+#endif
+
+class QPC_EXPORT CategoryGroup
+{
+ friend class Categories;
+public:
+ CategoryGroup(): mIdLabelMap(), mLabelIdMap() { }
+ CategoryGroup( const CategoryGroup &c ) :
+ mIdLabelMap( c.mIdLabelMap), mLabelIdMap( c.mLabelIdMap ) { }
+
+ void clear() { mIdLabelMap.clear(); mLabelIdMap.clear(); }
+
+ int add( const QString &label );
+ bool add( int uid, const QString &label );
+
+ bool remove( const QString &label );
+ bool remove( int uid );
+
+ bool rename( int uid, const QString &newLabel );
+ bool rename( const QString &oldLabel, const QString &newLabel );
+
+ bool contains(int id) const;
+ bool contains(const QString &label) const;
+
+ /** Returns label associated with the uid or QString::null if
+ * not found
+ */
+ const QString &label(int id) const;
+ /** Returns the uid associated with label or 0 if not found */
+ int id(const QString &label) const;
+
+ /** Returns a sorted list of labels */
+ QStringList labels() const;
+ QArray<int> ids( const QStringList &cats ) const;
+ QArray<int> ids() const;
+ QStringList labels( const QArray<int> &catids ) const;
+
+ const QMap<int, QString> &idMap() const { return mIdLabelMap; }
+
+private:
+ void insert( int uid, const QString &label );
+ QMap<int, QString> mIdLabelMap;
+ QMap<QString, int> mLabelIdMap;
+
+ static Qtopia::UidGen &uidGen() { return sUidGen; }
+ static Qtopia::UidGen sUidGen;
+};
+
+/** Map from application name to categories */
+class QPC_EXPORT Categories : public QObject
+{
+ Q_OBJECT
+public:
+ Categories( QObject *parent=0, const char *name = 0 )
+ : QObject( parent, name ), mGlobalCats(), mAppCats() { }
+ Categories( const Categories &copyFrom ) : QObject( copyFrom.parent() ),
+ mGlobalCats( copyFrom.mGlobalCats ),
+ mAppCats( copyFrom.mAppCats ) { }
+ virtual ~Categories() { }
+
+ Categories &operator= ( const Categories &c )
+{ mAppCats = c.mAppCats; mGlobalCats = c.mGlobalCats; return *this; }
+
+ void clear();
+
+ /** Add the category name as long as it doesn't already exist
+ * locally or globally. Return UID if added, 0 if conflicts
+ * (error).
+ */
+ int addCategory( const QString &appname, const QString &catname);
+ /** Add the category name as long as it doesn't already exist
+ * locally or globally. Return UID if added, 0 if conflicts
+ * (error).
+ */
+ int addCategory( const QString &appname, const QString &catname, int uid);
+ /** Add the global category just checking that it doesn't
+ * already exist globally. Return UID if added, 0 if conflicts.
+ */
+ int addGlobalCategory( const QString &catname );
+ /** Add the global category just checking that it doesn't
+ * already exist globally. Return UID if added, 0 if conflicts.
+ */
+ int addGlobalCategory( const QString &catname, int uid );
+ /** Removes the category from the application; if it is not found
+ * in the application, then it removes it from the global list
+ */
+ bool removeCategory( const QString &appName, const QString &catName,
+ bool checkGlobal = TRUE);
+ bool removeCategory( const QString &appName, int uid );
+ bool removeGlobalCategory( const QString &catName );
+ bool removeGlobalCategory( int uid );
+
+ QArray<int> ids( const QString &app ) const;
+ QArray<int> ids( const QString &app,
+ const QStringList &cats ) const;
+ /** Returns the id associated with the app */
+ int id( const QString &app, const QString &cat ) const;
+ /** Returns the label associated with the id */
+ QString label( const QString &app, int id ) const;
+
+ enum ExtraLabels { NoExtra, AllUnfiled, AllLabel, UnfiledLabel };
+ /** Returns the sorted list of all categories that are
+ * associated with the app.
+ * If includeGlobal parameter is TRUE then the returned
+ * categories will include the global category items.
+ * If extra = NoExtra, then
+ * If extra = AllUnfiled, then All and Unfiled will be prepended to
+ * the list
+ * If extra = AllLabel, then All is prepended
+ * If extra = UnfiledLabel, then Unfiled is prepended
+ */
+ QStringList labels( const QString &app,
+ bool includeGlobal = TRUE,
+ ExtraLabels extra = NoExtra ) const;
+
+ /** Returns the labels of the categories associated with the uids */
+ QStringList labels( const QString & app,
+ const QArray<int> &catids ) const;
+
+ enum DisplaySingle { ShowMulti, ShowAll, ShowFirst };
+
+ /** Returns a single string associated with the cat ids for display in
+ * a combobox or any area that requires one string. If catids are empty
+ * then "Unfiled" will be returned. If multiple categories are assigned
+ * then the behavior depends on the DisplaySingle type.
+ * If /a display is set to ShowMulti then " (multi)" appended to the
+ * first string. If /a display is set to ShowAll, then a space seperated
+ * string is returned with all categories. If ShowFirst is returned,
+ * the just the first string is returned.
+ */
+ QString displaySingle( const QString &app,
+ const QArray<int> &catids,
+ DisplaySingle display ) const;
+
+ QStringList globalCategories() const { return mGlobalCats.labels();}
+
+ bool renameCategory( const QString &appname,
+ const QString &oldName,
+ const QString &newName );
+ bool renameGlobalCategory( const QString &oldName,
+ const QString &newName );
+
+ void setGlobal( const QString &appname, const QString &catname,
+ bool value );
+ bool isGlobal( const QString &catname ) const;
+
+
+ /** Returns true if the catname is associated with any application
+ */
+ bool exists( const QString &catname ) const;
+ bool exists( const QString &appname, const QString &catname) const;
+
+ bool save( const QString &fname ) const;
+ bool load( const QString &fname );
+
+ // for debugging
+ void dump() const;
+
+ const QMap<QString, CategoryGroup> &appGroupMap() const{ return mAppCats; }
+ const CategoryGroup &globalGroup() const { return mGlobalCats; }
+
+signals:
+ /** emitted if added a category;
+ * the second param is the application the category was added to
+ * or null if global
+ * the third param is the uid of the newly added category
+ */
+ void categoryAdded( const Categories &, const QString &, int );
+ /** emitted if removed a category
+ * the second param is the application the category was removed from
+ * or null if global
+ * the third param is the uid of the removed category
+ */
+ void categoryRemoved( const Categories &, const QString &, int );
+ /** emitted if a category is renamed; the second param is the uid of
+ * the removed category */
+ void categoryRenamed( const Categories &, const QString &, int );
+
+private:
+ CategoryGroup mGlobalCats;
+ QMap< QString, CategoryGroup > mAppCats;
+};
+
+class QPC_EXPORT CheckedListView : public QListView
+{
+public:
+ void addCheckableList( const QStringList &options );
+ void setChecked( const QStringList &checked );
+ QStringList checked() const;
+};
+
+#endif
diff --git a/library/backend/contact.cpp b/library/backend/contact.cpp
new file mode 100644
index 0000000..a5f10ab
--- a/dev/null
+++ b/library/backend/contact.cpp
@@ -0,0 +1,909 @@
+/**********************************************************************
+** Copyright (C) 2001 Trolltech AS. All rights reserved.
+**
+** This file is part of Qtopia Environment.
+**
+** This file may be distributed and/or modified under the terms of the
+** GNU General Public License version 2 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file.
+**
+** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+**
+** See http://www.trolltech.com/gpl/ for GPL licensing information.
+**
+** Contact info@trolltech.com if any conditions of this licensing are
+** not clear to you.
+**
+**********************************************************************/
+
+#include "contact.h"
+#include "vobject_p.h"
+#include "qfiledirect_p.h"
+
+#include <qpe/stringutil.h>
+#include <qpe/timeconversion.h>
+
+#include <qobject.h>
+#include <qregexp.h>
+#include <qstylesheet.h>
+#include <qfileinfo.h>
+
+#include <stdio.h>
+
+Qtopia::UidGen Contact::sUidGen( Qtopia::UidGen::Qtopia );
+
+Contact::Contact()
+ : Record(), mMap(), d( 0 )
+{
+}
+
+Contact::Contact( const QMap<int, QString> &fromMap ) :
+ Record(), mMap( fromMap ), d( 0 )
+{
+ QString cats = mMap[ Qtopia::AddressCategory ];
+ if ( !cats.isEmpty() )
+ setCategories( idsFromString( cats ) );
+ QString uidStr = find( Qtopia::AddressUid );
+ if ( uidStr.isEmpty() )
+ setUid( uidGen().generate() );
+ else
+ setUid( uidStr.toInt() );
+}
+
+Contact::~Contact()
+{
+}
+
+QMap<int, QString> Contact::toMap() const
+{
+ QMap<int, QString> map = mMap;
+ map.insert( Qtopia::AddressCategory, idsToString( categories() ));
+ return map;
+}
+
+/*!
+ Returns a rich text formatted QString of the Contact.
+*/
+QString Contact::toRichText() const
+{
+ QString text;
+ QString value, comp, state;
+
+ // name, jobtitle and company
+ if ( !(value = fullName()).isEmpty() )
+ text += "<b>" + Qtopia::escapeString(value) + "</b><br>";
+ if ( !(value = jobTitle()).isEmpty() )
+ text += Qtopia::escapeString(value) + "<br>";
+
+ comp = company();
+ if ( !(value = department()).isEmpty() ) {
+ text += Qtopia::escapeString(value);
+ if ( comp )
+ text += ", ";
+ else
+ text += "<br>";
+ }
+ if ( !comp.isEmpty() )
+ text += Qtopia::escapeString(comp) + "<br>";
+
+ // business address
+ if ( !businessStreet().isEmpty() || !businessCity().isEmpty() ||
+ !businessZip().isEmpty() || !businessCountry().isEmpty() ) {
+ text += "<br>";
+ text += QObject::tr( "<b>Work Address:</b>" );
+ text += "<br>";
+ }
+
+ if ( !(value = businessStreet()).isEmpty() )
+ text += Qtopia::escapeString(value) + "<br>";
+ state = businessState();
+ if ( !(value = businessCity()).isEmpty() ) {
+ text += Qtopia::escapeString(value);
+ if ( state )
+ text += ", " + Qtopia::escapeString(state);
+ text += "<br>";
+ } else if ( !state.isEmpty() )
+ text += Qtopia::escapeString(state) + "<br>";
+ if ( !(value = businessZip()).isEmpty() )
+ text += Qtopia::escapeString(value) + "<br>";
+ if ( !(value = businessCountry()).isEmpty() )
+ text += Qtopia::escapeString(value) + "<br>";
+
+ // home address
+ if ( !homeStreet().isEmpty() || !homeCity().isEmpty() ||
+ !homeZip().isEmpty() || !homeCountry().isEmpty() ) {
+ text += "<br>";
+ text += QObject::tr( "<b>Home Address:</b>" );
+ text += "<br>";
+ }
+
+ if ( !(value = homeStreet()).isEmpty() )
+ text += Qtopia::escapeString(value) + "<br>";
+ state = homeState();
+ if ( !(value = homeCity()).isEmpty() ) {
+ text += Qtopia::escapeString(value);
+ if ( !state.isEmpty() )
+ text += ", " + Qtopia::escapeString(state);
+ text += "<br>";
+ } else if (!state.isEmpty())
+ text += Qtopia::escapeString(state) + "<br>";
+ if ( !(value = homeZip()).isEmpty() )
+ text += Qtopia::escapeString(value) + "<br>";
+ if ( !(value = homeCountry()).isEmpty() )
+ text += Qtopia::escapeString(value) + "<br>";
+
+ // the others...
+ QString str;
+ str = emails();
+ if ( !str.isEmpty() )
+ text += "<b>" + QObject::tr("Email Addresses: ") + "</b>"
+ + Qtopia::escapeString(str) + "<br>";
+ str = homePhone();
+ if ( !str.isEmpty() )
+ text += "<b>" + QObject::tr("Home Phone: ") + "</b>"
+ + Qtopia::escapeString(str) + "<br>";
+ str = homeFax();
+ if ( !str.isEmpty() )
+ text += "<b>" + QObject::tr("Home Fax: ") + "</b>"
+ + Qtopia::escapeString(str) + "<br>";
+ str = homeMobile();
+ if ( !str.isEmpty() )
+ text += "<b>" + QObject::tr("Home Mobile: ") + "</b>"
+ + Qtopia::escapeString(str) + "<br>";
+ str = homeWebpage();
+ if ( !str.isEmpty() )
+ text += "<b>" + QObject::tr("Home Web Page: ") + "</b>"
+ + Qtopia::escapeString(str) + "<br>";
+ str = businessWebpage();
+ if ( !str.isEmpty() )
+ text += "<b>" + QObject::tr("Business Web Page: ") + "</b>"
+ + Qtopia::escapeString(str) + "<br>";
+ str = office();
+ if ( !str.isEmpty() )
+ text += "<b>" + QObject::tr("Office: ") + "</b>"
+ + Qtopia::escapeString(str) + "<br>";
+ str = businessPhone();
+ if ( !str.isEmpty() )
+ text += "<b>" + QObject::tr("Business Phone: ") + "</b>"
+ + Qtopia::escapeString(str) + "<br>";
+ str = businessFax();
+ if ( !str.isEmpty() )
+ text += "<b>" + QObject::tr("Business Fax: ") + "</b>"
+ + Qtopia::escapeString(str) + "<br>";
+ str = businessMobile();
+ if ( !str.isEmpty() )
+ text += "<b>" + QObject::tr("Business Mobile: ") + "</b>"
+ + Qtopia::escapeString(str) + "<br>";
+ str = businessPager();
+ if ( !str.isEmpty() )
+ text += "<b>" + QObject::tr("Business Pager: ") + "</b>"
+ + Qtopia::escapeString(str) + "<br>";
+ str = profession();
+ if ( !str.isEmpty() )
+ text += "<b>" + QObject::tr("Profession: ") + "</b>"
+ + Qtopia::escapeString(str) + "<br>";
+ str = assistant();
+ if ( !str.isEmpty() )
+ text += "<b>" + QObject::tr("Assistant: ") + "</b>"
+ + Qtopia::escapeString(str) + "<br>";
+ str = manager();
+ if ( !str.isEmpty() )
+ text += "<b>" + QObject::tr("Manager: ") + "</b>"
+ + Qtopia::escapeString(str) + "<br>";
+ str = gender();
+ if ( !str.isEmpty() && str.toInt() != 0 ) {
+ if ( str.toInt() == 1 )
+ str = QObject::tr( "Male" );
+ else if ( str.toInt() == 2 )
+ str = QObject::tr( "Female" );
+ text += "<b>" + QObject::tr("Gender: ") + "</b>" + str + "<br>";
+ }
+ str = spouse();
+ if ( !str.isEmpty() )
+ text += "<b>" + QObject::tr("Spouse: ") + "</b>"
+ + Qtopia::escapeString(str) + "<br>";
+ str = birthday();
+ if ( !str.isEmpty() )
+ text += "<b>" + QObject::tr("Birthday: ") + "</b>"
+ + Qtopia::escapeString(str) + "<br>";
+ str = anniversary();
+ if ( !str.isEmpty() )
+ text += "<b>" + QObject::tr("Anniversary: ") + "</b>"
+ + Qtopia::escapeString(str) + "<br>";
+ str = nickname();
+ if ( !str.isEmpty() )
+ text += "<b>" + QObject::tr("Nickname: ") + "</b>"
+ + Qtopia::escapeString(str) + "<br>";
+
+ // notes last
+ if ( (value = notes()) ) {
+ QRegExp reg("\n");
+
+ //QString tmp = Qtopia::escapeString(value);
+ QString tmp = QStyleSheet::convertFromPlainText(value);
+ //tmp.replace( reg, "<br>" );
+ text += "<br>" + tmp + "<br>";
+ }
+ return text;
+}
+
+void Contact::insert( int key, const QString &v )
+{
+ QString value = v.stripWhiteSpace();
+ if ( value.isEmpty() )
+ mMap.remove( key );
+ else
+ mMap.insert( key, value );
+}
+
+void Contact::replace( int key, const QString & v )
+{
+ QString value = v.stripWhiteSpace();
+ if ( value.isEmpty() )
+ mMap.remove( key );
+ else
+ mMap.replace( key, value );
+}
+
+QString Contact::find( int key ) const
+{
+ return mMap[key];
+}
+
+QString Contact::displayAddress( const QString &street,
+ const QString &city,
+ const QString &state,
+ const QString &zip,
+ const QString &country ) const
+{
+ QString s = street;
+ if ( !street.isEmpty() )
+ s+= "\n";
+ s += city;
+ if ( !city.isEmpty() && !state.isEmpty() )
+ s += ", ";
+ s += state;
+ if ( !state.isEmpty() && !zip.isEmpty() )
+ s += " ";
+ s += zip;
+ if ( !country.isEmpty() && !s.isEmpty() )
+ s += "\n";
+ s += country;
+ return s;
+}
+
+QString Contact::displayBusinessAddress() const
+{
+ return displayAddress( businessStreet(), businessCity(),
+ businessState(), businessZip(),
+ businessCountry() );
+}
+
+QString Contact::displayHomeAddress() const
+{
+ return displayAddress( homeStreet(), homeCity(),
+ homeState(), homeZip(),
+ homeCountry() );
+}
+
+QString Contact::fullName() const
+{
+ QString title = find( Qtopia::Title );
+ QString firstName = find( Qtopia::FirstName );
+ QString middleName = find( Qtopia::MiddleName );
+ QString lastName = find( Qtopia::LastName );
+ QString suffix = find( Qtopia::Suffix );
+
+ QString name = title;
+ if ( !firstName.isEmpty() ) {
+ if ( !name.isEmpty() )
+ name += " ";
+ name += firstName;
+ }
+ if ( !middleName.isEmpty() ) {
+ if ( !name.isEmpty() )
+ name += " ";
+ name += middleName;
+ }
+ if ( !lastName.isEmpty() ) {
+ if ( !name.isEmpty() )
+ name += " ";
+ name += lastName;
+ }
+ if ( !suffix.isEmpty() ) {
+ if ( !name.isEmpty() )
+ name += " ";
+ name += suffix;
+ }
+ return name.simplifyWhiteSpace();
+}
+
+QStringList Contact::childrenList() const
+{
+ return QStringList::split( " ", find( Qtopia::Children ) );
+}
+
+QStringList Contact::emailList() const
+{
+ return QStringList::split( ";", find( Qtopia::Emails ) );
+}
+
+void Contact::setFileAs()
+{
+ QString lastName, firstName, middleName, fileas;
+
+ lastName = find( Qtopia::LastName );
+ firstName = find( Qtopia::FirstName );
+ middleName = find( Qtopia::MiddleName );
+ if ( !lastName.isEmpty() && !firstName.isEmpty()
+ && !middleName.isEmpty() )
+ fileas = lastName + ", " + firstName + " " + middleName;
+ else if ( !lastName.isEmpty() && !firstName.isEmpty() )
+ fileas = lastName + ", " + firstName;
+ else if ( !lastName.isEmpty() || !firstName.isEmpty() ||
+ !middleName.isEmpty() )
+ fileas = firstName + ( firstName.isEmpty() ? "" : " " )
+ + middleName + ( middleName.isEmpty() ? "" : " " )
+ + lastName;
+
+ replace( Qtopia::FileAs, fileas );
+}
+
+void Contact::save( QString &buf ) const
+{
+ static const QStringList SLFIELDS = fields();
+ // I'm expecting "<Contact " in front of this...
+ for ( QMap<int, QString>::ConstIterator it = mMap.begin();
+ it != mMap.end(); ++it ) {
+ const QString &value = it.data();
+ int key = it.key();
+ if ( !value.isEmpty() ) {
+ if ( key == Qtopia::AddressCategory || key == Qtopia::AddressUid)
+ continue;
+
+ key -= Qtopia::AddressCategory+1;
+ buf += SLFIELDS[key];
+ buf += "=\"" + Qtopia::escapeString(value) + "\" ";
+ }
+ }
+ buf += customToXml();
+ if ( categories().count() > 0 )
+ buf += "Categories=\"" + idsToString( categories() ) + "\" ";
+ buf += "Uid=\"" + QString::number( uid() ) + "\" ";
+ // You need to close this yourself
+}
+
+QStringList Contact::fields()
+{
+ QStringList list;
+
+ list.append( "Title" ); // Not Used!
+ list.append( "FirstName" );
+ list.append( "MiddleName" );
+ list.append( "LastName" );
+ list.append( "Suffix" );
+ list.append( "FileAs" );
+
+ list.append( "DefaultEmail" );
+ list.append( "Emails" );
+
+ list.append( "HomeStreet" );
+ list.append( "HomeCity" );
+ list.append( "HomeState" );
+ list.append( "HomeZip" );
+ list.append( "HomeCountry" );
+ list.append( "HomePhone" );
+ list.append( "HomeFax" );
+ list.append( "HomeMobile" );
+ list.append( "HomeWebPage" );
+
+ list.append( "Company" );
+ list.append( "BusinessStreet" );
+ list.append( "BusinessCity" );
+ list.append( "BusinessState" );
+ list.append( "BusinessZip" );
+ list.append( "BusinessCountry" );
+ list.append( "BusinessWebPage" );
+ list.append( "JobTitle" );
+ list.append( "Department" );
+ list.append( "Office" );
+ list.append( "BusinessPhone" );
+ list.append( "BusinessFax" );
+ list.append( "BusinessMobile" );
+ list.append( "BusinessPager" );
+ list.append( "Profession" );
+ list.append( "Assistant" );
+ list.append( "Manager" );
+
+ list.append( "Spouse" );
+ list.append( "Gender" );
+ list.append( "Birthday" );
+ list.append( "Anniversary" );
+ list.append( "Nickname" );
+
+ list.append( "Children" );
+ list.append( "Notes" );
+
+ return list;
+}
+
+QStringList Contact::trfields()
+{
+ QStringList list;
+
+ list.append( QObject::tr( "Name Title") );
+ list.append( QObject::tr( "First Name" ) );
+ list.append( QObject::tr( "Middle Name" ) );
+ list.append( QObject::tr( "Last Name" ) );
+ list.append( QObject::tr( "Suffix" ) );
+ list.append( QObject::tr( "File As" ) );
+
+ list.append( QObject::tr( "Default Email" ) );
+ list.append( QObject::tr( "Emails" ) );
+
+ list.append( QObject::tr( "Home Street" ) );
+ list.append( QObject::tr( "Home City" ) );
+ list.append( QObject::tr( "Home State" ) );
+ list.append( QObject::tr( "Home Zip" ) );
+ list.append( QObject::tr( "Home Country" ) );
+ list.append( QObject::tr( "Home Phone" ) );
+ list.append( QObject::tr( "Home Fax" ) );
+ list.append( QObject::tr( "Home Mobile" ) );
+ list.append( QObject::tr( "Home Web Page" ) );
+
+ list.append( QObject::tr( "Company" ) );
+ list.append( QObject::tr( "Business Street" ) );
+ list.append( QObject::tr( "Business City" ) );
+ list.append( QObject::tr( "Business State" ) );
+ list.append( QObject::tr( "Business Zip" ) );
+ list.append( QObject::tr( "Business Country" ) );
+ list.append( QObject::tr( "Business WebPage" ) );
+ list.append( QObject::tr( "Job Title" ) );
+ list.append( QObject::tr( "Department" ) );
+ list.append( QObject::tr( "Office" ) );
+ list.append( QObject::tr( "Business Phone" ) );
+ list.append( QObject::tr( "Business Fax" ) );
+ list.append( QObject::tr( "Business Mobile" ) );
+ list.append( QObject::tr( "Business Pager" ) );
+ list.append( QObject::tr( "Profession" ) );
+ list.append( QObject::tr( "Assistant" ) );
+ list.append( QObject::tr( "Manager" ) );
+
+ list.append( QObject::tr( "Spouse" ) );
+ list.append( QObject::tr( "Gender" ) );
+ list.append( QObject::tr( "Birthday" ) );
+ list.append( QObject::tr( "Anniversary" ) );
+ list.append( QObject::tr( "Nickname" ) );
+
+ list.append( QObject::tr( "Children" ) );
+ list.append( QObject::tr( "Notes" ) );
+
+ return list;
+}
+
+void Contact::setEmails( const QString &v )
+{
+ replace( Qtopia::Emails, v );
+ if ( v.isEmpty() )
+ setDefaultEmail( QString::null );
+}
+
+void Contact::setChildren( const QString &v )
+{
+ replace( Qtopia::Children, v );
+}
+
+// vcard conversion code
+static inline VObject *safeAddPropValue( VObject *o, const char *prop, const QString &value )
+{
+ VObject *ret = 0;
+ if ( o && !value.isEmpty() )
+ ret = addPropValue( o, prop, value.latin1() );
+ return ret;
+}
+
+static inline VObject *safeAddProp( VObject *o, const char *prop)
+{
+ VObject *ret = 0;
+ if ( o )
+ ret = addProp( o, prop );
+ return ret;
+}
+
+static VObject *createVObject( const Contact &c )
+{
+ VObject *vcard = newVObject( VCCardProp );
+ safeAddPropValue( vcard, VCVersionProp, "2.1" );
+ safeAddPropValue( vcard, VCLastRevisedProp, TimeConversion::toISO8601( QDateTime::currentDateTime() ) );
+ safeAddPropValue( vcard, VCUniqueStringProp, QString::number(c.uid()) );
+
+ // full name
+ safeAddPropValue( vcard, VCFullNameProp, c.fullName() );
+
+ // name properties
+ VObject *name = safeAddProp( vcard, VCNameProp );
+ safeAddPropValue( name, VCFamilyNameProp, c.lastName() );
+ safeAddPropValue( name, VCGivenNameProp, c.firstName() );
+ safeAddPropValue( name, VCAdditionalNamesProp, c.middleName() );
+ safeAddPropValue( name, VCNamePrefixesProp, c.title() );
+ safeAddPropValue( name, VCNameSuffixesProp, c.suffix() );
+
+ // home properties
+ VObject *home_adr= safeAddProp( vcard, VCAdrProp );
+ safeAddProp( home_adr, VCHomeProp );
+ safeAddPropValue( home_adr, VCStreetAddressProp, c.homeStreet() );
+ safeAddPropValue( home_adr, VCCityProp, c.homeCity() );
+ safeAddPropValue( home_adr, VCRegionProp, c.homeState() );
+ safeAddPropValue( home_adr, VCPostalCodeProp, c.homeZip() );
+ safeAddPropValue( home_adr, VCCountryNameProp, c.homeCountry() );
+
+ VObject *home_phone = safeAddPropValue( vcard, VCTelephoneProp, c.homePhone() );
+ safeAddProp( home_phone, VCHomeProp );
+ home_phone = safeAddPropValue( vcard, VCTelephoneProp, c.homeMobile() );
+ safeAddProp( home_phone, VCHomeProp );
+ safeAddProp( home_phone, VCCellularProp );
+ home_phone = safeAddPropValue( vcard, VCTelephoneProp, c.homeFax() );
+ safeAddProp( home_phone, VCHomeProp );
+ safeAddProp( home_phone, VCFaxProp );
+
+ VObject *url = safeAddPropValue( vcard, VCURLProp, c.homeWebpage() );
+ safeAddProp( url, VCHomeProp );
+
+ // work properties
+ VObject *work_adr= safeAddProp( vcard, VCAdrProp );
+ safeAddProp( work_adr, VCWorkProp );
+ safeAddPropValue( work_adr, VCStreetAddressProp, c.businessStreet() );
+ safeAddPropValue( work_adr, VCCityProp, c.businessCity() );
+ safeAddPropValue( work_adr, VCRegionProp, c.businessState() );
+ safeAddPropValue( work_adr, VCPostalCodeProp, c.businessZip() );
+ safeAddPropValue( work_adr, VCCountryNameProp, c.businessCountry() );
+
+ VObject *work_phone = safeAddPropValue( vcard, VCTelephoneProp, c.businessPhone() );
+ safeAddProp( work_phone, VCWorkProp );
+ work_phone = safeAddPropValue( vcard, VCTelephoneProp, c.businessMobile() );
+ safeAddProp( work_phone, VCWorkProp );
+ safeAddProp( work_phone, VCCellularProp );
+ work_phone = safeAddPropValue( vcard, VCTelephoneProp, c.businessFax() );
+ safeAddProp( work_phone, VCWorkProp );
+ safeAddProp( work_phone, VCFaxProp );
+ work_phone = safeAddPropValue( vcard, VCTelephoneProp, c.businessPager() );
+ safeAddProp( work_phone, VCWorkProp );
+ safeAddProp( work_phone, VCPagerProp );
+
+ url = safeAddPropValue( vcard, VCURLProp, c.businessWebpage() );
+ safeAddProp( url, VCWorkProp );
+
+ VObject *title = safeAddPropValue( vcard, VCTitleProp, c.jobTitle() );
+ safeAddProp( title, VCWorkProp );
+
+
+ QStringList emails = c.emailList();
+ emails.prepend( c.defaultEmail() );
+ for( QStringList::Iterator it = emails.begin(); it != emails.end(); ++it ) {
+ VObject *email = safeAddPropValue( vcard, VCEmailAddressProp, *it );
+ safeAddProp( email, VCInternetProp );
+ }
+
+ safeAddPropValue( vcard, VCNoteProp, c.notes() );
+
+ safeAddPropValue( vcard, VCBirthDateProp, c.birthday() );
+
+ if ( !c.company().isEmpty() || !c.department().isEmpty() || !c.office().isEmpty() ) {
+ VObject *org = safeAddProp( vcard, VCOrgProp );
+ safeAddPropValue( org, VCOrgNameProp, c.company() );
+ safeAddPropValue( org, VCOrgUnitProp, c.department() );
+ safeAddPropValue( org, VCOrgUnit2Prop, c.office() );
+ }
+
+ // some values we have to export as custom fields
+ safeAddPropValue( vcard, "X-Qtopia-Profession", c.profession() );
+ safeAddPropValue( vcard, "X-Qtopia-Manager", c.manager() );
+ safeAddPropValue( vcard, "X-Qtopia-Assistant", c.assistant() );
+
+ safeAddPropValue( vcard, "X-Qtopia-Spouse", c.spouse() );
+ safeAddPropValue( vcard, "X-Qtopia-Gender", c.gender() );
+ safeAddPropValue( vcard, "X-Qtopia-Anniversary", c.anniversary() );
+ safeAddPropValue( vcard, "X-Qtopia-Nickname", c.nickname() );
+ safeAddPropValue( vcard, "X-Qtopia-Children", c.children() );
+
+ return vcard;
+}
+
+
+static Contact parseVObject( VObject *obj )
+{
+ Contact c;
+
+ bool haveDefaultEmail = FALSE;
+
+ VObjectIterator it;
+ initPropIterator( &it, obj );
+ while( moreIteration( &it ) ) {
+ VObject *o = nextVObject( &it );
+ QCString name = vObjectName( o );
+ QCString value = vObjectStringZValue( o );
+ if ( name == VCNameProp ) {
+ VObjectIterator nit;
+ initPropIterator( &nit, o );
+ while( moreIteration( &nit ) ) {
+ VObject *o = nextVObject( &nit );
+ QCString name = vObjectName( o );
+ QString value = vObjectStringZValue( o );
+ if ( name == VCNamePrefixesProp )
+ c.setTitle( value );
+ else if ( name == VCNameSuffixesProp )
+ c.setSuffix( value );
+ else if ( name == VCFamilyNameProp )
+ c.setLastName( value );
+ else if ( name == VCGivenNameProp )
+ c.setFirstName( value );
+ else if ( name == VCAdditionalNamesProp )
+ c.setMiddleName( value );
+ }
+ }
+ else if ( name == VCAdrProp ) {
+ bool work = TRUE; // default address is work address
+ QString street;
+ QString city;
+ QString region;
+ QString postal;
+ QString country;
+
+ VObjectIterator nit;
+ initPropIterator( &nit, o );
+ while( moreIteration( &nit ) ) {
+ VObject *o = nextVObject( &nit );
+ QCString name = vObjectName( o );
+ QString value = vObjectStringZValue( o );
+ if ( name == VCHomeProp )
+ work = FALSE;
+ else if ( name == VCWorkProp )
+ work = TRUE;
+ else if ( name == VCStreetAddressProp )
+ street = value;
+ else if ( name == VCCityProp )
+ city = value;
+ else if ( name == VCRegionProp )
+ region = value;
+ else if ( name == VCPostalCodeProp )
+ postal = value;
+ else if ( name == VCCountryNameProp )
+ country = value;
+ }
+ if ( work ) {
+ c.setBusinessStreet( street );
+ c.setBusinessCity( city );
+ c.setBusinessCountry( country );
+ c.setBusinessZip( postal );
+ c.setBusinessState( region );
+ } else {
+ c.setHomeStreet( street );
+ c.setHomeCity( city );
+ c.setHomeCountry( country );
+ c.setHomeZip( postal );
+ c.setHomeState( region );
+ }
+ }
+ else if ( name == VCTelephoneProp ) {
+ enum {
+ HOME = 0x01,
+ WORK = 0x02,
+ VOICE = 0x04,
+ CELL = 0x08,
+ FAX = 0x10,
+ PAGER = 0x20,
+ UNKNOWN = 0x80
+ };
+ int type = 0;
+
+ VObjectIterator nit;
+ initPropIterator( &nit, o );
+ while( moreIteration( &nit ) ) {
+ VObject *o = nextVObject( &nit );
+ QCString name = vObjectName( o );
+ if ( name == VCHomeProp )
+ type |= HOME;
+ else if ( name == VCWorkProp )
+ type |= WORK;
+ else if ( name == VCVoiceProp )
+ type |= VOICE;
+ else if ( name == VCCellularProp )
+ type |= CELL;
+ else if ( name == VCFaxProp )
+ type |= FAX;
+ else if ( name == VCPagerProp )
+ type |= PAGER;
+ else if ( name == VCPreferredProp )
+ ;
+ else
+ type |= UNKNOWN;
+ }
+ if ( (type & UNKNOWN) != UNKNOWN ) {
+ if ( ( type & (HOME|WORK) ) == 0 ) // default
+ type |= HOME;
+ if ( ( type & (VOICE|CELL|FAX|PAGER) ) == 0 ) // default
+ type |= VOICE;
+
+ if ( (type & (VOICE|HOME) ) == (VOICE|HOME) )
+ c.setHomePhone( value );
+ if ( ( type & (FAX|HOME) ) == (FAX|HOME) )
+ c.setHomeFax( value );
+ if ( ( type & (CELL|HOME) ) == (CELL|HOME) )
+ c.setHomeMobile( value );
+ if ( ( type & (VOICE|WORK) ) == (VOICE|WORK) )
+ c.setBusinessPhone( value );
+ if ( ( type & (FAX|WORK) ) == (FAX|WORK) )
+ c.setBusinessFax( value );
+ if ( ( type & (CELL|WORK) ) == (CELL|WORK) )
+ c.setBusinessMobile( value );
+ if ( ( type & (PAGER|WORK) ) == (PAGER|WORK) )
+ c.setBusinessPager( value );
+ }
+ }
+ else if ( name == VCEmailAddressProp ) {
+ QString email = vObjectStringZValue( o );
+ bool valid = TRUE;
+ VObjectIterator nit;
+ initPropIterator( &nit, o );
+ while( moreIteration( &nit ) ) {
+ VObject *o = nextVObject( &nit );
+ QCString name = vObjectName( o );
+ if ( name != VCInternetProp && name != VCHomeProp &&
+ name != VCWorkProp &&
+ name != VCPreferredProp )
+ // ### preffered should map to default email
+ valid = FALSE;
+ }
+ if ( valid ) {
+ if ( haveDefaultEmail ) {
+ QString str = c.emails();
+ if ( !str.isEmpty() )
+ str += ","+email;
+ c.setEmails( str );
+ } else {
+ c.setDefaultEmail( email );
+ }
+ }
+ }
+ else if ( name == VCURLProp ) {
+ VObjectIterator nit;
+ initPropIterator( &nit, o );
+ while( moreIteration( &nit ) ) {
+ VObject *o = nextVObject( &nit );
+ QCString name = vObjectName( o );
+ if ( name == VCHomeProp )
+ c.setHomeWebpage( value );
+ else if ( name == VCWorkProp )
+ c.setBusinessWebpage( value );
+ }
+ }
+ else if ( name == VCOrgProp ) {
+ VObjectIterator nit;
+ initPropIterator( &nit, o );
+ while( moreIteration( &nit ) ) {
+ VObject *o = nextVObject( &nit );
+ QCString name = vObjectName( o );
+ QString value = vObjectStringZValue( o );
+ if ( name == VCOrgNameProp )
+ c.setCompany( value );
+ else if ( name == VCOrgUnitProp )
+ c.setDepartment( value );
+ else if ( name == VCOrgUnit2Prop )
+ c.setOffice( value );
+ }
+ }
+ else if ( name == VCTitleProp ) {
+ c.setJobTitle( value );
+ }
+ else if ( name == "X-Qtopia-Profession" ) {
+ c.setProfession( value );
+ }
+ else if ( name == "X-Qtopia-Manager" ) {
+ c.setManager( value );
+ }
+ else if ( name == "X-Qtopia-Assistant" ) {
+ c.setAssistant( value );
+ }
+ else if ( name == "X-Qtopia-Spouse" ) {
+ c.setSpouse( value );
+ }
+ else if ( name == "X-Qtopia-Gender" ) {
+ c.setGender( value );
+ }
+ else if ( name == "X-Qtopia-Anniversary" ) {
+ c.setAnniversary( value );
+ }
+ else if ( name == "X-Qtopia-Nickname" ) {
+ c.setNickname( value );
+ }
+ else if ( name == "X-Qtopia-Children" ) {
+ c.setChildren( value );
+ }
+
+
+#if 0
+ else {
+ printf("Name: %s, value=%s\n", name.data(), vObjectStringZValue( o ) );
+ VObjectIterator nit;
+ initPropIterator( &nit, o );
+ while( moreIteration( &nit ) ) {
+ VObject *o = nextVObject( &nit );
+ QCString name = vObjectName( o );
+ QString value = vObjectStringZValue( o );
+ printf(" subprop: %s = %s\n", name.data(), value.latin1() );
+ }
+ }
+#endif
+ }
+ c.setFileAs();
+ return c;
+}
+
+void Contact::writeVCard( const QString &filename, const QValueList<Contact> &contacts)
+{
+ QFileDirect f( filename.utf8().data() );
+ if ( !f.open( IO_WriteOnly ) ) {
+ qWarning("Unable to open vcard write");
+ return;
+ }
+
+ QValueList<Contact>::ConstIterator it;
+ for( it = contacts.begin(); it != contacts.end(); ++it ) {
+ VObject *obj = createVObject( *it );
+ writeVObject(f.directHandle() , obj );
+ cleanVObject( obj );
+ }
+ cleanStrTbl();
+}
+
+void Contact::writeVCard( const QString &filename, const Contact &contact)
+{
+ QFileDirect f( filename.utf8().data() );
+ if ( !f.open( IO_WriteOnly ) ) {
+ qWarning("Unable to open vcard write");
+ return;
+ }
+
+ VObject *obj = createVObject( contact );
+ writeVObject( f.directHandle() , obj );
+ cleanVObject( obj );
+
+ cleanStrTbl();
+}
+
+
+QValueList<Contact> Contact::readVCard( const QString &filename )
+{
+ qDebug("trying to open %s, exists=%d", filename.utf8().data(), QFileInfo( filename.utf8().data() ).size() );
+ VObject *obj = Parse_MIME_FromFileName( (char *)filename.utf8().data() );
+
+ qDebug("vobject = %p", obj );
+
+ QValueList<Contact> contacts;
+
+ while ( obj ) {
+ contacts.append( parseVObject( obj ) );
+
+ VObject *t = obj;
+ obj = nextVObjectInList(obj);
+ cleanVObject( t );
+ }
+
+ return contacts;
+}
+
+bool Contact::match( const QRegExp &r ) const
+{
+ bool match;
+ match = false;
+ QMap<int, QString>::ConstIterator it;
+ for ( it = mMap.begin(); it != mMap.end(); ++it ) {
+ if ( (*it).find( r ) > -1 ) {
+ match = true;
+ break;
+ }
+ }
+ return match;
+}
diff --git a/library/backend/contact.h b/library/backend/contact.h
new file mode 100644
index 0000000..6abdab6
--- a/dev/null
+++ b/library/backend/contact.h
@@ -0,0 +1,217 @@
+/**********************************************************************
+** Copyright (C) 2001 Trolltech AS. All rights reserved.
+**
+** This file is part of Qtopia Environment.
+**
+** This file may be distributed and/or modified under the terms of the
+** GNU General Public License version 2 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file.
+**
+** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+**
+** See http://www.trolltech.com/gpl/ for GPL licensing information.
+**
+** Contact info@trolltech.com if any conditions of this licensing are
+** not clear to you.
+**
+**********************************************************************/
+
+#ifndef __CONTACT_H__
+#define __CONTACT_H__
+
+#include <qpe/palmtoprecord.h>
+#include <qpe/recordfields.h>
+
+#include <qstringlist.h>
+
+#if defined(QPC_TEMPLATEDLL)
+// MOC_SKIP_BEGIN
+template class QPC_EXPORT QMap<int, QString>;
+// MOC_SKIP_END
+#endif
+
+class ContactPrivate;
+class QPC_EXPORT Contact : public Qtopia::Record
+{
+ friend class DataSet;
+public:
+ Contact();
+ Contact( const QMap<int, QString> &fromMap );
+ virtual ~Contact();
+
+ static void writeVCard( const QString &filename, const QValueList<Contact> &contacts);
+ static void writeVCard( const QString &filename, const Contact &c );
+ static QValueList<Contact> readVCard( const QString &filename );
+
+ enum journal_action { ACTION_ADD, ACTION_REMOVE, ACTION_REPLACE };
+
+ void setTitle( const QString &v ) { replace( Qtopia::Title, v ); }
+ void setFirstName( const QString &v ) { replace( Qtopia::FirstName, v ); }
+ void setMiddleName( const QString &v ) { replace( Qtopia::MiddleName, v ); }
+ void setLastName( const QString &v ) { replace( Qtopia::LastName, v ); }
+ void setSuffix( const QString &v ) { replace( Qtopia::Suffix, v ); }
+ void setFileAs( const QString &v ) { replace( Qtopia::FileAs, v ); }
+ void setFileAs();
+
+ // default email address
+ void setDefaultEmail( const QString &v ) { replace( Qtopia::DefaultEmail, v ); }
+ // the emails should be seperated by a semicolon
+ void setEmails( const QString &v );
+
+ // home
+ void setHomeStreet( const QString &v ) { replace( Qtopia::HomeStreet, v ); }
+ void setHomeCity( const QString &v ) { replace( Qtopia::HomeCity, v ); }
+ void setHomeState( const QString &v ) { replace( Qtopia::HomeState, v ); }
+ void setHomeZip( const QString &v ) { replace( Qtopia::HomeZip, v ); }
+ void setHomeCountry( const QString &v ) { replace( Qtopia::HomeCountry, v ); }
+ void setHomePhone( const QString &v ) { replace( Qtopia::HomePhone, v ); }
+ void setHomeFax( const QString &v ) { replace( Qtopia::HomeFax, v ); }
+ void setHomeMobile( const QString &v ) { replace( Qtopia::HomeMobile, v ); }
+ void setHomeWebpage( const QString &v ) { replace( Qtopia::HomeWebPage, v ); }
+
+ // business
+ void setCompany( const QString &v ) { replace( Qtopia::Company, v ); }
+ void setBusinessStreet( const QString &v ) { replace( Qtopia::BusinessStreet, v ); }
+ void setBusinessCity( const QString &v ) { replace( Qtopia::BusinessCity, v ); }
+ void setBusinessState( const QString &v ) { replace( Qtopia::BusinessState, v ); }
+ void setBusinessZip( const QString &v ) { replace( Qtopia::BusinessZip, v ); }
+ void setBusinessCountry( const QString &v ) { replace( Qtopia::BusinessCountry, v ); }
+ void setBusinessWebpage( const QString &v ) { replace( Qtopia::BusinessWebPage, v ); }
+ void setJobTitle( const QString &v ) { replace( Qtopia::JobTitle, v ); }
+ void setDepartment( const QString &v ) { replace( Qtopia::Department, v ); }
+ void setOffice( const QString &v ) { replace( Qtopia::Office, v ); }
+ void setBusinessPhone( const QString &v ) { replace( Qtopia::BusinessPhone, v ); }
+ void setBusinessFax( const QString &v ) { replace( Qtopia::BusinessFax, v ); }
+ void setBusinessMobile( const QString &v ) { replace( Qtopia::BusinessMobile, v ); }
+ void setBusinessPager( const QString &v ) { replace( Qtopia::BusinessPager, v ); }
+ void setProfession( const QString &v ) { replace( Qtopia::Profession, v ); }
+ void setAssistant( const QString &v ) { replace( Qtopia::Assistant, v ); }
+ void setManager( const QString &v ) { replace( Qtopia::Manager, v ); }
+
+ // personal
+ void setSpouse( const QString &v ) { replace( Qtopia::Spouse, v ); }
+ void setGender( const QString &v ) { replace( Qtopia::Gender, v ); }
+ void setBirthday( const QString &v ) { replace( Qtopia::Birthday, v ); }
+ void setAnniversary( const QString &v ) { replace( Qtopia::Anniversary, v ); }
+ void setNickname( const QString &v ) { replace( Qtopia::Nickname, v ); }
+ void setChildren( const QString &v );
+
+ // other
+ void setNotes( const QString &v ) { replace( Qtopia::Notes, v); }
+
+ bool match( const QRegExp &r ) const;
+
+// // custom
+// void setCustomField( const QString &key, const QString &v )
+// { replace(Custom- + key, v ); }
+
+ // name
+ QString fullName() const;
+ QString title() const { return find( Qtopia::Title ); }
+ QString firstName() const { return find( Qtopia::FirstName ); }
+ QString middleName() const { return find( Qtopia::MiddleName ); }
+ QString lastName() const { return find( Qtopia::LastName ); }
+ QString suffix() const { return find( Qtopia::Suffix ); }
+ QString fileAs() const { return find( Qtopia::FileAs ); }
+
+ // email
+ QString defaultEmail() const { return find( Qtopia::DefaultEmail ); }
+ QString emails() const { return find( Qtopia::Emails ); }
+ QStringList emailList() const;
+
+ // home
+ QString homeStreet() const { return find( Qtopia::HomeStreet ); }
+ QString homeCity() const { return find( Qtopia::HomeCity ); }
+ QString homeState() const { return find( Qtopia::HomeState ); }
+ QString homeZip() const { return find( Qtopia::HomeZip ); }
+ QString homeCountry() const { return find( Qtopia::HomeCountry ); }
+ QString homePhone() const { return find( Qtopia::HomePhone ); }
+ QString homeFax() const { return find( Qtopia::HomeFax ); }
+ QString homeMobile() const { return find( Qtopia::HomeMobile ); }
+ QString homeWebpage() const { return find( Qtopia::HomeWebPage ); }
+ /** Multi line string containing all non-empty address info in the form
+ * Street
+ * City, State Zip
+ * Country
+ */
+ QString displayHomeAddress() const;
+
+ // business
+ QString company() const { return find( Qtopia::Company ); }
+ QString businessStreet() const { return find( Qtopia::BusinessStreet ); }
+ QString businessCity() const { return find( Qtopia::BusinessCity ); }
+ QString businessState() const { return find( Qtopia::BusinessState ); }
+ QString businessZip() const { return find( Qtopia::BusinessZip ); }
+ QString businessCountry() const { return find( Qtopia::BusinessCountry ); }
+ QString businessWebpage() const { return find( Qtopia::BusinessWebPage ); }
+ QString jobTitle() const { return find( Qtopia::JobTitle ); }
+ QString department() const { return find( Qtopia::Department ); }
+ QString office() const { return find( Qtopia::Office ); }
+ QString businessPhone() const { return find( Qtopia::BusinessPhone ); }
+ QString businessFax() const { return find( Qtopia::BusinessFax ); }
+ QString businessMobile() const { return find( Qtopia::BusinessMobile ); }
+ QString businessPager() const { return find( Qtopia::BusinessPager ); }
+ QString profession() const { return find( Qtopia::Profession ); }
+ QString assistant() const { return find( Qtopia::Assistant ); }
+ QString manager() const { return find( Qtopia::Manager ); }
+ /** Multi line string containing all non-empty address info in the form
+ * Street
+ * City, State Zip
+ * Country
+ */
+ QString displayBusinessAddress() const;
+
+ //personal
+ QString spouse() const { return find( Qtopia::Spouse ); }
+ QString gender() const { return find( Qtopia::Gender ); }
+ QString birthday() const { return find( Qtopia::Birthday ); }
+ QString anniversary() const { return find( Qtopia::Anniversary ); }
+ QString nickname() const { return find( Qtopia::Nickname ); }
+ QString children() const { return find( Qtopia::Children ); }
+ QStringList childrenList() const;
+
+ // other
+ QString notes() const { return find( Qtopia::Notes ); }
+ QString groups() const { return find( Qtopia::Groups ); }
+ QStringList groupList() const;
+
+// // custom
+// const QString &customField( const QString &key )
+// { return find( Custom- + key ); }
+
+ static QStringList fields();
+ static QStringList trfields();
+
+ QString toRichText() const;
+ QMap<int, QString> toMap() const;
+ QString field( int key ) const { return find( key ); }
+
+
+ // journaling...
+ void saveJournal( journal_action action, const QString &key = QString::null );
+ void save( QString &buf ) const;
+
+ void setUid( int i )
+{ Record::setUid(i); replace( Qtopia::AddressUid , QString::number(i)); }
+
+private:
+ friend class AbTable;
+ void insert( int key, const QString &value );
+ void replace( int key, const QString &value );
+ QString find( int key ) const;
+
+ QString displayAddress( const QString &street,
+ const QString &city,
+ const QString &state,
+ const QString &zip,
+ const QString &country ) const;
+
+ Qtopia::UidGen &uidGen() { return sUidGen; }
+ static Qtopia::UidGen sUidGen;
+ QMap<int, QString> mMap;
+ ContactPrivate *d;
+};
+
+#endif
diff --git a/library/backend/event.cpp b/library/backend/event.cpp
new file mode 100644
index 0000000..50a663d
--- a/dev/null
+++ b/library/backend/event.cpp
@@ -0,0 +1,830 @@
+/**********************************************************************
+** Copyright (C) 2001 Trolltech AS. All rights reserved.
+**
+** This file is part of Qtopia Environment.
+**
+** This file may be distributed and/or modified under the terms of the
+** GNU General Public License version 2 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file.
+**
+** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+**
+** See http://www.trolltech.com/gpl/ for GPL licensing information.
+**
+** Contact info@trolltech.com if any conditions of this licensing are
+** not clear to you.
+**
+**********************************************************************/
+
+#include "event.h"
+#include "qfiledirect_p.h"
+#include <qpe/timeconversion.h>
+#include <qpe/stringutil.h>
+#include <qpe/recordfields.h>
+#include <qbuffer.h>
+#include <time.h>
+#include "vobject_p.h"
+
+#include <stdio.h>
+
+using namespace Qtopia;
+
+static void write( QString& buf, const Event::RepeatPattern &r )
+{
+ buf += " rtype=\"";
+ switch ( r.type ) {
+ case Event::Daily:
+ buf += "Daily";
+ break;
+ case Event::Weekly:
+ buf += "Weekly";
+ break;
+ case Event::MonthlyDay:
+ buf += "MonthlyDay";
+ break;
+ case Event::MonthlyDate:
+ buf += "MonthlyDate";
+ break;
+ case Event::Yearly:
+ buf += "Yearly";
+ break;
+ default:
+ buf += "NoRepeat";
+ break;
+ }
+ buf += "\"";
+ if ( r.days > 0 )
+ buf += " rweekdays=\"" + QString::number( static_cast<int>( r.days ) ) + "\"";
+ if ( r.position != 0 )
+ buf += " rposition=\"" + QString::number( r.position ) + "\"";
+
+ buf += " rfreq=\"" + QString::number( r.frequency ) + "\"";
+ buf += " rhasenddate=\"" + QString::number( static_cast<int>( r.hasEndDate ) ) + "\"";
+ if ( r.hasEndDate )
+ buf += " enddt=\""
+ + QString::number( r.endDateUTC ? r.endDateUTC : time( 0 ) )
+ + "\"";
+ buf += " created=\"" + QString::number( r.createTime ) + "\"";
+}
+
+Qtopia::UidGen Event::sUidGen( Qtopia::UidGen::Qtopia );
+
+Event::Event() : Record()
+{
+ startUTC = endUTC = time( 0 );
+ typ = Normal;
+ hAlarm = FALSE;
+ hRepeat = FALSE;
+ aMinutes = 0;
+ aSound = Silent;
+ pattern.type = NoRepeat;
+ pattern.frequency = -1;
+}
+
+Event::Event( const QMap<int, QString> &map )
+{
+ setDescription( map[DatebookDescription] );
+ setLocation( map[Location] );
+ setCategories( idsFromString( map[DatebookCategory] ) );
+ setTimeZone( map[TimeZone] );
+ setNotes( map[Note] );
+ setStart( TimeConversion::fromUTC( map[StartDateTime].toUInt() ) );
+ setEnd( TimeConversion::fromUTC( map[EndDateTime].toUInt() ) );
+ setType( (Event::Type) map[DatebookType].toInt() );
+ setAlarm( ( map[HasAlarm] == "1" ? TRUE : FALSE ), map[AlarmTime].toInt(), (Event::SoundTypeChoice)map[SoundType].toInt() );
+ Event::RepeatPattern p;
+ p.type = (Event::RepeatType) map[ RepeatPatternType ].toInt();
+ p.frequency = map[ RepeatPatternFrequency ].toInt();
+ p.position = map[ RepeatPatternPosition ].toInt();
+ p.days = map[ RepeatPatternDays ].toInt();
+ p.hasEndDate = map[ RepeatPatternHasEndDate ].toInt();
+ p.endDateUTC = map[ RepeatPatternEndDate ].toUInt();
+ setRepeat( p );
+
+ setUid( map[ DatebookUid ].toInt() );
+}
+
+Event::~Event()
+{
+}
+
+int Event::week( const QDate& date )
+{
+ // Calculates the week this date is in within that
+ // month. Equals the "row" is is in in the month view
+ int week = 1;
+ QDate tmp( date.year(), date.month(), 1 );
+
+ if ( date.dayOfWeek() < tmp.dayOfWeek() )
+ ++week;
+
+ week += ( date.day() - 1 ) / 7;
+ return week;
+}
+
+int Event::occurrence( const QDate& date )
+{
+ // calculates the number of occurrances of this day of the
+ // week till the given date (e.g 3rd Wednesday of the month)
+ return ( date.day() - 1 ) / 7 + 1;
+}
+
+int Event::dayOfWeek( char day )
+{
+ int dayOfWeek = 1;
+ char i = Event::MON;
+ while ( !( i & day ) && i <= Event::SUN ) {
+ i <<= 1;
+ ++dayOfWeek;
+ }
+ return dayOfWeek;
+}
+
+int Event::monthDiff( const QDate& first, const QDate& second )
+{
+ return ( second.year() - first.year() ) * 12 +
+ second.month() - first.month();
+}
+
+QMap<int, QString> Event::toMap() const
+{
+ QMap<int, QString> m;
+ m.insert( DatebookDescription, description() );
+ m.insert ( Location, location() );
+ m.insert ( DatebookCategory, idsToString( categories() ) );
+ m.insert ( TimeZone, timeZone() );
+ m.insert ( Note, notes() );
+ m.insert ( StartDateTime, QString::number( TimeConversion::toUTC( start() ) ) );
+ m.insert ( EndDateTime, QString::number( TimeConversion::toUTC( end() ) ) );
+ m.insert ( DatebookType, QString::number( (int)type() ) );
+ m.insert ( HasAlarm, ( hasAlarm() ? "1" : "0" ) );
+ m.insert ( SoundType, QString::number( (int)alarmSound() ) );
+ m.insert ( AlarmTime, QString::number( alarmTime() ) );
+ m.insert ( RepeatPatternType, QString::number( static_cast<int>( repeatPattern().type ) ) );
+ m.insert ( RepeatPatternFrequency, QString::number( repeatPattern().frequency ) );
+ m.insert ( RepeatPatternPosition, QString::number( repeatPattern().position ) );
+ m.insert ( RepeatPatternDays, QString::number( repeatPattern().days ) );
+ m.insert ( RepeatPatternHasEndDate, QString::number( static_cast<int>( repeatPattern().hasEndDate ) ) );
+ m.insert ( RepeatPatternEndDate, QString::number( repeatPattern().endDateUTC ) );
+
+ m.insert( DatebookUid, QString::number( uid()) );
+
+ return m;
+}
+
+void Event::setRepeat( const RepeatPattern &p )
+{
+ setRepeat( p.type != NoRepeat, p );
+}
+
+void Event::setDescription( const QString &s )
+{
+ descript = s;
+}
+
+void Event::setLocation( const QString &s )
+{
+ locat = s;
+}
+
+// void Event::setCategory( const QString &s )
+// {
+// categ = s;
+// }
+
+void Event::setType( Type t )
+{
+ typ = t;
+}
+
+void Event::setStart( const QDateTime &d )
+{
+ startUTC = TimeConversion::toUTC( d );
+}
+
+void Event::setStart( time_t time )
+{
+ startUTC = time;
+}
+
+void Event::setEnd( const QDateTime &d )
+{
+ endUTC = TimeConversion::toUTC( d );
+}
+
+void Event::setEnd( time_t time )
+{
+ endUTC = time;
+}
+
+void Event::setTimeZone( const QString &z )
+{
+ tz = z;
+}
+
+void Event::setAlarm( bool b, int minutes, SoundTypeChoice s )
+{
+ hAlarm = b;
+ aMinutes = minutes;
+ aSound = s;
+}
+
+void Event::setRepeat( bool b, const RepeatPattern &p )
+{
+ hRepeat = b;
+ pattern = p;
+}
+
+void Event::setNotes( const QString &n )
+{
+ note = n;
+}
+
+const QString &Event::description() const
+{
+ return descript;
+}
+
+const QString &Event::location() const
+{
+ return locat;
+}
+
+// QString Event::category() const
+// {
+// return categ;
+// }
+
+Event::Type Event::type() const
+{
+ return typ;
+}
+
+QDateTime Event::start( bool actual ) const
+{
+ QDateTime dt = (startUTC > 0) ? TimeConversion::fromUTC( startUTC ) : QDateTime::currentDateTime();
+
+ if ( actual && typ == AllDay ) {
+ QTime t = dt.time();
+ t.setHMS( 0, 0, 0 );
+ dt.setTime( t );
+ }
+ return dt;
+}
+
+QDateTime Event::end( bool actual ) const
+{
+ QDateTime dt = (endUTC > 0) ? TimeConversion::fromUTC( endUTC ) : QDateTime::currentDateTime();
+
+ if ( actual && typ == AllDay ) {
+ QTime t = dt.time();
+ t.setHMS( 23, 59, 59 );
+ dt.setTime( t );
+ }
+ return dt;
+}
+
+const QString &Event::timeZone() const
+{
+ return tz;
+}
+
+bool Event::hasAlarm() const
+{
+ return hAlarm;
+}
+
+int Event::alarmTime() const
+{
+ return aMinutes;
+}
+
+Event::SoundTypeChoice Event::alarmSound() const
+{
+ return aSound;
+}
+
+bool Event::hasRepeat() const
+{
+ return doRepeat();
+}
+
+const Event::RepeatPattern &Event::repeatPattern() const
+{
+ return pattern;
+}
+
+Event::RepeatPattern &Event::repeatPattern()
+{
+ return pattern;
+}
+
+const QString &Event::notes() const
+{
+ return note;
+}
+
+bool Event::operator==( const Event &e ) const
+{
+ return ( e.descript == descript &&
+ e.locat == locat &&
+ e.categ == categ &&
+ e.typ == typ &&
+ e.startUTC == startUTC &&
+ e.endUTC == endUTC &&
+ e.tz == tz &&
+ e.hAlarm == hAlarm &&
+ e.aMinutes == aMinutes &&
+ e.aSound == aSound &&
+ e.hRepeat == hRepeat &&
+ e.pattern == pattern &&
+ e.note == note );
+}
+
+void Event::save( QString& buf )
+{
+ buf += " description=\"" + Qtopia::escapeString(descript) + "\"";
+ if ( !locat.isEmpty() )
+ buf += " location=\"" + Qtopia::escapeString(locat) + "\"";
+ // save the categoies differently....
+ QString strCats = idsToString( categories() );
+ buf += " categories=\"" + Qtopia::escapeString(strCats) + "\"";
+ buf += " uid=\"" + QString::number( uid() ) + "\"";
+ if ( (Type)typ != Normal )
+ buf += " type=\"AllDay\"";
+ if ( hAlarm ) {
+ buf += " alarm=\"" + QString::number( aMinutes ) + "\" sound=\"";
+ if ( aSound == Event::Loud )
+ buf += "loud";
+ else
+ buf += "silent";
+ buf += "\"";
+ }
+ if ( hRepeat )
+ write( buf, pattern );
+
+ buf += " start=\""
+ + QString::number( startUTC )
+ + "\"";
+
+ buf += " end=\""
+ + QString::number( endUTC )
+ + "\"";
+
+ if ( !note.isEmpty() )
+ buf += " note=\"" + Qtopia::escapeString( note ) + "\"";
+ buf += customToXml();
+}
+
+bool Event::RepeatPattern::operator==( const Event::RepeatPattern &right ) const
+{
+ // *sigh*
+ return ( type == right.type
+ && frequency == right.frequency
+ && position == right.position
+ && days == right.days
+ && hasEndDate == right.hasEndDate
+ && endDateUTC == right.endDateUTC
+ && createTime == right.createTime );
+}
+
+
+class EffectiveEventPrivate
+{
+public:
+ //currently the existence of the d pointer means multi-day repeating,
+ //msut be changed if we use the d pointer for anything else.
+ QDate startDate;
+ QDate endDate;
+};
+
+
+EffectiveEvent::EffectiveEvent()
+{
+ mDate = QDate::currentDate();
+ mStart = mEnd = QTime::currentTime();
+ d = 0;
+}
+
+EffectiveEvent::EffectiveEvent( const Event &e, const QDate &date, Position pos )
+{
+ mEvent = e;
+ mDate = date;
+ if ( pos & Start )
+ mStart = e.start( TRUE ).time();
+ else
+ mStart = QTime( 0, 0, 0 );
+
+ if ( pos & End )
+ mEnd = e.end( TRUE ).time();
+ else
+ mEnd = QTime( 23, 59, 59 );
+ d = 0;
+}
+
+EffectiveEvent::~EffectiveEvent()
+{
+ delete d;
+}
+
+EffectiveEvent::EffectiveEvent( const EffectiveEvent &e )
+{
+ d = 0;
+ *this = e;
+}
+
+EffectiveEvent& EffectiveEvent::operator=( const EffectiveEvent & e )
+{
+ if ( &e == this )
+ return *this;
+ delete d;
+ if ( e.d ) {
+ d = new EffectiveEventPrivate;
+ d->startDate = e.d->startDate;
+ d->endDate = e.d->endDate;
+ } else {
+ d = 0;
+ }
+ mEvent = e.mEvent;
+ mDate = e.mDate;
+ mStart = e.mStart;
+ mEnd = e.mEnd;
+
+ return *this;
+
+}
+
+// QString EffectiveEvent::category() const
+// {
+// return mEvent.category();
+// }
+
+const QString &EffectiveEvent::description( ) const
+{
+ return mEvent.description();
+}
+
+const QString &EffectiveEvent::location( ) const
+{
+ return mEvent.location();
+}
+
+const QString &EffectiveEvent::notes() const
+{
+ return mEvent.notes();
+}
+
+const Event &EffectiveEvent::event() const
+{
+ return mEvent;
+}
+
+const QTime &EffectiveEvent::end() const
+{
+ return mEnd;
+}
+
+const QTime &EffectiveEvent::start() const
+{
+ return mStart;
+}
+
+const QDate &EffectiveEvent::date() const
+{
+ return mDate;
+}
+
+int EffectiveEvent::length() const
+{
+ return (mEnd.hour() * 60 - mStart.hour() * 60)
+ + QABS(mStart.minute() - mEnd.minute() );
+}
+
+void EffectiveEvent::setDate( const QDate &dt )
+{
+ mDate = dt;
+}
+
+void EffectiveEvent::setStart( const QTime &start )
+{
+ mStart = start;
+}
+
+void EffectiveEvent::setEnd( const QTime &end )
+{
+ mEnd = end;
+}
+
+void EffectiveEvent::setEvent( Event e )
+{
+ mEvent = e;
+}
+
+bool EffectiveEvent::operator<( const EffectiveEvent &e ) const
+{
+ if ( mDate < e.date() )
+ return TRUE;
+ if ( mDate == e.date() )
+ return ( mStart < e.start() );
+ else
+ return FALSE;
+}
+
+bool EffectiveEvent::operator<=( const EffectiveEvent &e ) const
+{
+ return (mDate <= e.date() );
+}
+
+bool EffectiveEvent::operator==( const EffectiveEvent &e ) const
+{
+ return ( mDate == e.date()
+ && mStart == e.start()
+ && mEnd == e.end()
+ && mEvent == e.event() );
+}
+
+bool EffectiveEvent::operator!=( const EffectiveEvent &e ) const
+{
+ return !(*this == e);
+}
+
+bool EffectiveEvent::operator>( const EffectiveEvent &e ) const
+{
+ return !(*this <= e );
+}
+
+bool EffectiveEvent::operator>=(const EffectiveEvent &e) const
+{
+ return !(*this < e);
+}
+
+void EffectiveEvent::setEffectiveDates( const QDate &from, const QDate &to )
+{
+ if ( !from.isValid() ) {
+ delete d;
+ d = 0;
+ return;
+ }
+ if ( !d )
+ d = new EffectiveEventPrivate;
+ d->startDate = from;
+ d->endDate = to;
+}
+
+QDate EffectiveEvent::startDate() const
+{
+ if ( d )
+ return d->startDate;
+ else if ( mEvent.hasRepeat() )
+ return mDate; // single day, since multi-day should have a d pointer
+ else
+ return mEvent.start().date();
+}
+
+QDate EffectiveEvent::endDate() const
+{
+ if ( d )
+ return d->endDate;
+ else if ( mEvent.hasRepeat() )
+ return mDate; // single day, since multi-day should have a d pointer
+ else
+ return mEvent.end().date();
+}
+
+int EffectiveEvent::size() const
+{
+ return ( mEnd.hour() - mStart.hour() ) * 3600
+ + (mEnd.minute() - mStart.minute() * 60
+ + mEnd.second() - mStart.second() );
+}
+
+
+// vcal conversion code
+static inline VObject *safeAddPropValue( VObject *o, const char *prop, const QString &value )
+{
+ VObject *ret = 0;
+ if ( o && !value.isEmpty() )
+ ret = addPropValue( o, prop, value.latin1() );
+ return ret;
+}
+
+static inline VObject *safeAddProp( VObject *o, const char *prop)
+{
+ VObject *ret = 0;
+ if ( o )
+ ret = addProp( o, prop );
+ return ret;
+}
+
+static VObject *createVObject( const Event &e )
+{
+ VObject *vcal = newVObject( VCCalProp );
+ safeAddPropValue( vcal, VCVersionProp, "1.0" );
+ VObject *event = safeAddProp( vcal, VCEventProp );
+
+ safeAddPropValue( event, VCDTstartProp, TimeConversion::toISO8601( e.start() ) );
+ safeAddPropValue( event, VCDTendProp, TimeConversion::toISO8601( e.end() ) );
+ safeAddPropValue( event, "X-Qtopia-NOTES", e.description() );
+ safeAddPropValue( event, VCDescriptionProp, e.description() );
+ safeAddPropValue( event, VCLocationProp, e.location() );
+
+ if ( e.hasAlarm() ) {
+ VObject *alarm = safeAddProp( event, VCAAlarmProp );
+ QDateTime dt = e.start();
+ dt = dt.addSecs( -e.alarmTime()*60 );
+ safeAddPropValue( alarm, VCRunTimeProp, TimeConversion::toISO8601( dt ) );
+ safeAddPropValue( alarm, VCAudioContentProp,
+ (e.alarmSound() == Event::Silent ? "silent" : "alarm" ) );
+ }
+
+ safeAddPropValue( event, "X-Qtopia-TIMEZONE", e.timeZone() );
+
+ if ( e.type() == Event::AllDay )
+ safeAddPropValue( event, "X-Qtopia-AllDay", e.timeZone() );
+
+ // ### repeat missing
+
+ // ### categories missing
+
+ return vcal;
+}
+
+
+static Event parseVObject( VObject *obj )
+{
+ Event e;
+
+ bool haveAlarm = FALSE;
+ bool haveStart = FALSE;
+ bool haveEnd = FALSE;
+ QDateTime alarmTime;
+ Event::SoundTypeChoice soundType = Event::Silent;
+
+ VObjectIterator it;
+ initPropIterator( &it, obj );
+ while( moreIteration( &it ) ) {
+ VObject *o = nextVObject( &it );
+ QCString name = vObjectName( o );
+ QCString value = vObjectStringZValue( o );
+ if ( name == VCDTstartProp ) {
+ e.setStart( TimeConversion::fromISO8601( value ) );
+ haveStart = TRUE;
+ }
+ else if ( name == VCDTendProp ) {
+ e.setEnd( TimeConversion::fromISO8601( value ) );
+ haveEnd = TRUE;
+ }
+ else if ( name == "X-Qtopia-NOTES" ) {
+ e.setNotes( value );
+ }
+ else if ( name == VCDescriptionProp ) {
+ e.setDescription( value );
+ }
+ else if ( name == VCLocationProp ) {
+ e.setLocation( value );
+ }
+ else if ( name == VCAudioContentProp ) {
+ haveAlarm = TRUE;
+ VObjectIterator nit;
+ initPropIterator( &nit, o );
+ while( moreIteration( &nit ) ) {
+ VObject *o = nextVObject( &nit );
+ QCString name = vObjectName( o );
+ QCString value = vObjectStringZValue( o );
+ if ( name == VCRunTimeProp )
+ alarmTime = TimeConversion::fromISO8601( value );
+ else if ( name == VCAudioContentProp ) {
+ if ( value == "silent" )
+ soundType = Event::Silent;
+ else
+ soundType = Event::Loud;
+ }
+ }
+ }
+ else if ( name == "X-Qtopia-TIMEZONE") {
+ e.setTimeZone( value );
+ }
+ else if ( name == "X-Qtopia-AllDay" ) {
+ e.setType( Event::AllDay );
+ }
+#if 0
+ else {
+ printf("Name: %s, value=%s\n", name.data(), vObjectStringZValue( o ) );
+ VObjectIterator nit;
+ initPropIterator( &nit, o );
+ while( moreIteration( &nit ) ) {
+ VObject *o = nextVObject( &nit );
+ QCString name = vObjectName( o );
+ QString value = vObjectStringZValue( o );
+ printf(" subprop: %s = %s\n", name.data(), value.latin1() );
+ }
+ }
+#endif
+ }
+
+ if ( !haveStart && !haveEnd )
+ e.setStart( QDateTime::currentDateTime() );
+
+ if ( !haveEnd ) {
+ e.setType( Event::AllDay );
+ e.setEnd( e.start() );
+ }
+
+ if ( haveAlarm ) {
+ int minutes = alarmTime.secsTo( e.start() ) / 60;
+ e.setAlarm( TRUE, minutes, soundType );
+ }
+ return e;
+}
+
+
+
+void Event::writeVCalendar( const QString &filename, const QValueList<Event> &events)
+{
+ QFileDirect f( filename.utf8().data() );
+ if ( !f.open( IO_WriteOnly ) ) {
+ qWarning("Unable to open vcard write");
+ return;
+ }
+
+ QValueList<Event>::ConstIterator it;
+ for( it = events.begin(); it != events.end(); ++it ) {
+ VObject *obj = createVObject( *it );
+ writeVObject( f.directHandle() , obj );
+ cleanVObject( obj );
+ }
+
+ cleanStrTbl();
+}
+
+void Event::writeVCalendar( const QString &filename, const Event &event)
+{
+ QFileDirect f( filename.utf8().data() );
+ if ( !f.open( IO_WriteOnly ) ) {
+ qWarning("Unable to open vcard write");
+ return;
+ }
+
+ VObject *obj = createVObject( event );
+ writeVObject( f.directHandle() , obj );
+ cleanVObject( obj );
+
+ cleanStrTbl();
+}
+
+
+QValueList<Event> Event::readVCalendar( const QString &filename )
+{
+ VObject *obj = Parse_MIME_FromFileName( (char *)filename.utf8().data() );
+
+ QValueList<Event> events;
+
+ while ( obj ) {
+ QCString name = vObjectName( obj );
+ if ( name == VCCalProp ) {
+ VObjectIterator nit;
+ initPropIterator( &nit, obj );
+ while( moreIteration( &nit ) ) {
+ VObject *o = nextVObject( &nit );
+ QCString name = vObjectName( o );
+ if ( name == VCEventProp )
+ events.append( parseVObject( o ) );
+ }
+ } else if ( name == VCEventProp ) {
+ // shouldn't happen, but just to be sure
+ events.append( parseVObject( obj ) );
+ }
+ VObject *t = obj;
+ obj = nextVObjectInList(obj);
+ cleanVObject( t );
+ }
+
+ return events;
+}
+
+bool Event::match( const QRegExp &r ) const
+{
+ bool returnMe;
+ returnMe = false;
+
+ if ( descript.find( r ) > -1 )
+ returnMe = true;
+ else if ( locat.find( r ) > -1 )
+ returnMe = true;
+ else if ( TimeConversion::fromUTC( startUTC ).toString().find( r ) > -1 )
+ returnMe = true;
+ else if ( TimeConversion::fromUTC( endUTC ).toString().find( r ) > -1 )
+ returnMe = true;
+ else if ( tz.find( r ) > -1 )
+ returnMe = true;
+ else if ( note.find( r ) > -1 )
+ returnMe = true;
+ else if ( doRepeat() ) {
+ if ( pattern.hasEndDate )
+ if ( TimeConversion::fromUTC( pattern.endDateUTC ).toString().find(r) > -1 )
+ returnMe = true;
+ }
+ return returnMe;
+}
diff --git a/library/backend/event.h b/library/backend/event.h
new file mode 100644
index 0000000..0ebe9ea
--- a/dev/null
+++ b/library/backend/event.h
@@ -0,0 +1,229 @@
+/**********************************************************************
+** Copyright (C) 2001 Trolltech AS. All rights reserved.
+**
+** This file is part of Qtopia Environment.
+**
+** This file may be distributed and/or modified under the terms of the
+** GNU General Public License version 2 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file.
+**
+** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+**
+** See http://www.trolltech.com/gpl/ for GPL licensing information.
+**
+** Contact info@trolltech.com if any conditions of this licensing are
+** not clear to you.
+**
+**********************************************************************/
+
+#ifndef __EVENT_H__
+#define __EVENT_H__
+
+#include <qdatetime.h>
+#include <qvaluelist.h>
+
+#ifdef PALMTOPCENTER
+#include <qpc/qsorter.h>
+#endif
+#include <qpe/palmtoprecord.h>
+
+#include <qpe/timeconversion.h>
+
+class EventPrivate;
+class QPC_EXPORT Event : public Qtopia::Record
+{
+public:
+ enum RepeatType { NoRepeat = -1, Daily, Weekly, MonthlyDay,
+ MonthlyDate, Yearly };
+ enum Days { MON = 0x01, TUE = 0x02, WED = 0x04, THU = 0x08,
+ FRI = 0x10, SAT = 0x20, SUN = 0x40 };
+ struct QPC_EXPORT RepeatPattern
+ {
+ RepeatPattern() {
+ type = NoRepeat; frequency = -1; days = 0; position = 0; createTime = -1;
+ hasEndDate = FALSE; endDateUTC = 0; }
+ bool operator ==( const RepeatPattern &right ) const;
+
+ RepeatType type;
+ int frequency;
+ int position; // the posistion in the month (e.g. the first sunday, etc) positive, count from the front negative count from the end...
+ char days; // a mask for days OR in your days!
+ bool hasEndDate;
+ QDate endDate() const { return TimeConversion::fromUTC( endDateUTC ).date(); }
+ void setEndDate( const QDate &dt ) { endDateUTC = TimeConversion::toUTC( dt ); }
+ time_t endDateUTC;
+ time_t createTime;
+ };
+
+ Event();
+ Event( const QMap<int, QString > & map );
+ virtual ~Event();
+
+ QMap<int, QString> toMap() const;
+
+ static void writeVCalendar( const QString &filename, const QValueList<Event> &events);
+ static void writeVCalendar( const QString &filename, const Event &event);
+ static QValueList<Event> readVCalendar( const QString &filename );
+
+ enum Type { Normal, AllDay };
+ enum SoundTypeChoice { Silent, Loud };
+
+ bool operator<( const Event &e1) const { return start() < e1.start(); };
+ bool operator<=( const Event &e1 ) const { return start() <= e1.start(); };
+ bool operator!=( const Event &e1 ) const { return !( *this == e1 ); };
+ bool operator>( const Event &e1 ) const { return start() > e1.start(); };
+ bool operator>=(const Event &e1 ) const { return start() >= e1.start(); };
+ bool operator==( const Event &e ) const;
+
+ void setDescription( const QString &s );
+ const QString &description() const;
+
+ void setLocation( const QString &s );
+ const QString &location() const;
+
+ void setType( Type t );
+ Type type() const;
+ void setStart( const QDateTime &d );
+ void setStart( time_t time );
+ QDateTime start( bool actual = FALSE ) const;
+ time_t startTime() const { return startUTC; }
+ void setEnd( const QDateTime &e );
+ void setEnd( time_t time );
+ QDateTime end( bool actual = FALSE ) const;
+ time_t endTime() const { return endUTC; }
+ void setTimeZone( const QString & );
+ const QString &timeZone() const;
+ void setAlarm( bool b, int minutes, SoundTypeChoice );
+ bool hasAlarm() const;
+ int alarmTime() const;
+ SoundTypeChoice alarmSound() const;
+ void setRepeat( bool b, const RepeatPattern &p );
+ void setRepeat( const RepeatPattern &p );
+ bool hasRepeat() const;
+ const RepeatPattern &repeatPattern() const;
+ RepeatPattern &repeatPattern();
+ void setNotes( const QString &n );
+ const QString &notes() const;
+ bool doRepeat() const { return pattern.type != NoRepeat; }
+
+ void save( QString& buf );
+ //void load( Node *n );
+
+ // helper function to calculate the week of the given date
+ static int week( const QDate& date );
+ // calculates the number of occurrences of the week day of
+ // the given date from the start of the month
+ static int occurrence( const QDate& date );
+ // returns a proper days-char for a given dayOfWeek()
+ static char day( int dayOfWeek ) { return 1 << ( dayOfWeek - 1 ); }
+ // returns the dayOfWeek for the *first* day it finds (ignores
+ // any further days!). Returns 1 (Monday) if there isn't any day found
+ static int dayOfWeek( char day );
+ // returns the difference of months from first to second.
+ static int monthDiff( const QDate& first, const QDate& second );
+ bool match( const QRegExp &r ) const;
+
+private:
+ Qtopia::UidGen &uidGen() { return sUidGen; }
+ static Qtopia::UidGen sUidGen;
+
+ QString descript, locat, categ;
+ Type typ : 4;
+ bool startTimeDirty : 1;
+ bool endTimeDirty : 1;
+ time_t startUTC, endUTC;
+ QString tz;
+ bool hAlarm, hRepeat;
+ int aMinutes;
+ SoundTypeChoice aSound;
+ RepeatPattern pattern;
+ QString note;
+ EventPrivate *d;
+};
+
+// Since an event spans multiple day, it is better to have this
+// class to represent a day instead of creating many
+// dummy events...
+
+class EffectiveEventPrivate;
+class QPC_EXPORT EffectiveEvent
+{
+public:
+ // If we calculate the effective event of a multi-day event
+ // we have to figure out whether we are at the first day,
+ // at the end, or anywhere else ("middle"). This is important
+ // for the start/end times (00:00/23:59)
+ // MidWay: 00:00 -> 23:59, as we are "in the middle" of a multi-
+ // day event
+ // Start: start time -> 23:59
+ // End: 00:00 -> end time
+ // Start | End == StartEnd: for single-day events (default)
+ // here we draw start time -> end time
+ enum Position { MidWay = 0, Start = 1, End = 2, StartEnd = 3 };
+
+ EffectiveEvent();
+ EffectiveEvent( const Event &event, const QDate &startDate, Position pos = StartEnd );
+ EffectiveEvent( const EffectiveEvent & );
+ EffectiveEvent& operator=( const EffectiveEvent & );
+ ~EffectiveEvent();
+
+
+ bool operator<( const EffectiveEvent &e ) const;
+ bool operator<=( const EffectiveEvent &e ) const;
+ bool operator==( const EffectiveEvent &e ) const;
+ bool operator!=( const EffectiveEvent &e ) const;
+ bool operator>( const EffectiveEvent &e ) const;
+ bool operator>= ( const EffectiveEvent &e ) const;
+
+ void setStart( const QTime &start );
+ void setEnd( const QTime &end );
+ void setEvent( Event e );
+ void setDate( const QDate &date );
+ void setEffectiveDates( const QDate &from, const QDate &to );
+
+ // QString category() const;
+ const QString &description() const;
+ const QString &location() const;
+ const QString &notes() const;
+ const Event &event() const;
+ const QTime &start() const;
+ const QTime &end() const;
+ const QDate &date() const;
+ int length() const;
+ int size() const;
+
+ QDate startDate() const;
+ QDate endDate() const;
+
+private:
+ class EffectiveEventPrivate *d;
+ Event mEvent;
+ QDate mDate;
+ QTime mStart,
+ mEnd;
+
+};
+
+#ifdef PALMTOPCENTER
+class QPC_EXPORT EffectiveEventSizeSorter : public QSorter<EffectiveEvent>
+{
+public:
+ int compare( const EffectiveEvent& a, const EffectiveEvent& b ) const
+ {
+ return a.size() - b.size();
+ }
+};
+
+class QPC_EXPORT EffectiveEventTimeSorter : public QSorter<EffectiveEvent>
+{
+public:
+ int compare( const EffectiveEvent& a, const EffectiveEvent& b ) const
+ {
+ return a.start().secsTo( b.start() );
+ }
+};
+#endif
+
+#endif
diff --git a/library/backend/palmtoprecord.cpp b/library/backend/palmtoprecord.cpp
new file mode 100644
index 0000000..0d57699
--- a/dev/null
+++ b/library/backend/palmtoprecord.cpp
@@ -0,0 +1,127 @@
+/**********************************************************************
+** Copyright (C) 2000 Trolltech AS. All rights reserved.
+**
+** This file is part of Qtopia Environment.
+**
+** This file may be distributed and/or modified under the terms of the
+** GNU General Public License version 2 as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included
+** in the packaging of this file.
+**
+** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING
+** THE WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A
+** PARTICULAR PURPOSE.
+**
+** See http://www.trolltech.com/gpl/ for GPL licensing information.
+**
+** Contact info@trolltech.com if any conditions of this licensing are
+** not clear to you.
+**
+**********************************************************************/
+#include "palmtoprecord.h"
+#include "stringutil.h"
+#include <qstringlist.h>
+
+namespace Qtopia {
+
+Record &Record::operator=( const Record &c )
+{
+ mUid = c.mUid;
+ mCats = c.mCats;
+ customMap = c.customMap;
+ return *this;
+}
+
+void Record::setCategories( int single )
+{
+ if ( single == 0 )
+ return;
+ mCats.resize(1);
+ mCats[0] = single;
+}
+
+// convenience methods provided for loading and saving to xml
+QString Record::idsToString( const QArray<int> &cats )
+{
+ QString str;
+ for ( uint i = 0; i < cats.size(); i++ )
+ if ( i == 0 )
+ str = QString::number( cats[int(i)] );
+ else
+ str += ";" + QString::number( cats[int(i)] );
+
+ return str;
+}
+
+// convenience methods provided for loading and saving to xml
+QArray<int> Record::idsFromString( const QString &str )
+{
+ QStringList catStrs = QStringList::split( ";", str );
+ QArray<int> cats( catStrs.count() );
+ uint i = 0;
+ for ( QStringList::ConstIterator it = catStrs.begin();
+ it != catStrs.end(); ++it ) {
+ cats[int(i)] = (*it).toInt();
+ i++;
+ }
+ return cats;
+}
+
+/*!
+ Returns the string stored for the custom field \a key.
+ Returns a null string if the field does not exist.
+ */
+QString Record::customField( const QString &key) const
+{
+ if (customMap.contains(key))
+ return customMap[key];
+
+ return QString::null;
+}
+
+/*!
+ Sets the string stored for the custom field \a key to \a value.
+ */
+void Record::setCustomField( const QString &key, const QString &value)
+{
+ qWarning("setting custom " + key + " to " + value);
+ if (customMap.contains(key))
+ customMap.replace(key, value);
+ else
+ customMap.insert(key, value);
+
+ qWarning(QString("custom size %1").arg(customMap.count()));
+}
+
+/*!
+ Removes the custom field \a key.
+ */
+void Record::removeCustomField(const QString &key)
+{
+ customMap.remove(key);
+}
+
+QString Record::customToXml() const
+{
+ //qWarning(QString("writing custom %1").arg(customMap.count()));
+ QString buf(" ");
+ for ( QMap<QString, QString>::ConstIterator cit = customMap.begin();
+ cit != customMap.end(); ++cit) {
+ qWarning(".ITEM.");
+ buf += cit.key();
+ buf += "=\"";
+ buf += escapeString(cit.data());
+ buf += "\" ";
+ }
+ return buf;
+}
+
+void Record::dump( const QMap<int, QString> &map )
+{
+ QMap<int, QString>::ConstIterator it;
+ for( it = map.begin(); it != map.end(); ++it )
+ qDebug("%d : %s", it.key(), it.data().local8Bit().data() );
+}
+
+}
+
diff --git a/library/backend/palmtoprecord.h b/library/backend/palmtoprecord.h
new file mode 100644
index 0000000..0372011
--- a/dev/null
+++ b/library/backend/palmtoprecord.h
@@ -0,0 +1,94 @@
+/**********************************************************************
+** Copyright (C) 2000 Trolltech AS. All rights reserved.
+**
+** This file is part of Qtopia Environment.
+**
+** This file may be distributed and/or modified under the terms of the
+** GNU General Public License version 2 as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included
+** in the packaging of this file.
+**
+** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING
+** THE WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A
+** PARTICULAR PURPOSE.
+**
+** See http://www.trolltech.com/gpl/ for GPL licensing information.
+**
+** Contact info@trolltech.com if any conditions of this licensing are
+** not clear to you.
+**
+**********************************************************************/
+
+#ifndef QTPALMTOP_RECORD_H
+#define QTPALMTOP_RECORD_H
+
+#include <qglobal.h>
+#include "qpcglobal.h"
+#include "palmtopuidgen.h"
+#include <qarray.h>
+#include <qmap.h>
+
+#if defined(QPC_TEMPLATEDLL)
+// MOC_SKIP_BEGIN
+template class QPC_EXPORT QMap<QString, QString>;
+// MOC_SKIP_END
+#endif
+
+class QRegExp;
+namespace Qtopia {
+
+class RecordPrivate;
+class QPC_EXPORT Record
+{
+public:
+ Record() : mUid(0), mCats() { }
+ Record( const Record &c ) : mUid( c.mUid ), mCats ( c.mCats ), customMap(c.customMap) { }
+ virtual ~Record() { }
+
+ Record &operator=( const Record &c );
+
+ virtual bool match( const QRegExp & ) const { return FALSE; }
+
+ void setCategories( const QArray<int> &v ) { mCats = v; }
+ void setCategories( int single );
+ const QArray<int> &categories() const { return mCats; }
+
+ int uid() const { return mUid; };
+ virtual void setUid( int i ) { mUid = i; uidGen().store( mUid ); }
+ bool isValidUid() const { return mUid != 0; }
+ void assignUid() { setUid( uidGen().generate() ); }
+
+ virtual QString customField(const QString &) const;
+ virtual void setCustomField(const QString &, const QString &);
+ virtual void removeCustomField(const QString &);
+
+ virtual bool operator == ( const Record &r ) const
+{ return mUid == r.mUid; }
+ virtual bool operator != ( const Record &r ) const
+{ return mUid != r.mUid; }
+
+ // convenience methods provided for loading and saving to xml
+ static QString idsToString( const QArray<int> &ids );
+ // convenience methods provided for loading and saving to xml
+ static QArray<int> idsFromString( const QString &str );
+
+ // for debugging
+ static void dump( const QMap<int, QString> &map );
+
+protected:
+ virtual UidGen &uidGen() = 0;
+
+ virtual QString customToXml() const;
+
+private:
+ int mUid;
+ QArray<int> mCats;
+
+ QMap<QString, QString> customMap;
+
+ RecordPrivate *d;
+};
+
+}
+
+#endif
diff --git a/library/backend/palmtopuidgen.h b/library/backend/palmtopuidgen.h
new file mode 100644
index 0000000..1a16681
--- a/dev/null
+++ b/library/backend/palmtopuidgen.h
@@ -0,0 +1,83 @@
+#ifndef QTPALMTOP_UIDGEN_H
+#define QTPALMTOP_UIDGEN_H
+/**********************************************************************
+** Copyright (C) 2000 Trolltech AS. All rights reserved.
+**
+** This file is part of Qtopia Environment.
+**
+** Licensees holding valid Qtopia Developer license may use this
+** file in accordance with the Qtopia Developer License Agreement
+** provided with the Software.
+**
+** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING
+** THE WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+** PURPOSE.
+**
+** email sales@trolltech.com for information about Qtopia License
+** Agreements.
+**
+** Contact info@trolltech.com if any conditions of this licensing are
+** not clear to you.
+**
+**********************************************************************/
+
+#include <time.h>
+#include <qmap.h>
+#include "qpcglobal.h"
+
+#if defined(QPC_TEMPLATEDLL)
+// MOC_SKIP_BEGIN
+template class QPC_EXPORT QMap< int, bool >;
+// MOC_SKIP_END
+#endif
+
+namespace Qtopia {
+
+
+class QPC_EXPORT UidGen
+{
+public:
+ enum Type { Qtopia, PalmtopCenter };
+
+ UidGen() : type( Qtopia ), sign( -1 ), ids()
+{
+#ifdef PALMTOPCENTER
+ type = PalmtopCenter;
+ sign = 1;
+#endif
+}
+ UidGen( Type t ) : type(t), sign(1), ids()
+{
+ if ( t == Qtopia )
+ sign = -1;
+}
+
+ virtual ~UidGen() { }
+
+ int generate() const
+{
+ int id = sign * (int) ::time(NULL);
+ while ( ids.contains( id ) ) {
+ id += sign;
+
+ // check for overflow cases; if so, wrap back to beginning of
+ // set ( -1 or 1 )
+ if ( sign == -1 && id > 0 || sign == 1 && id < 0 )
+ id = sign;
+ }
+ return id;
+}
+
+ void store(int id) { ids.insert(id, TRUE); }
+ bool isUnique(int id) const { return (!ids.contains(id)); }
+
+private:
+ Type type;
+ int sign;
+ QMap<int, bool> ids;
+
+};
+
+}
+
+#endif
diff --git a/library/backend/qfiledirect_p.h b/library/backend/qfiledirect_p.h
new file mode 100644
index 0000000..fc29ac5
--- a/dev/null
+++ b/library/backend/qfiledirect_p.h
@@ -0,0 +1,36 @@
+/**********************************************************************
+** Copyright (C) 2001 Trolltech AS. All rights reserved.
+**
+** This file is part of the Qtopia Environment.
+**
+** Licensees holding valid Qtopia Developer license may use this
+** file in accordance with the Qtopia Developer License Agreement
+** provided with the Software.
+**
+** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING
+** THE WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+** PURPOSE.
+**
+** email sales@trolltech.com for information about Qtopia License
+** Agreements.
+**
+** Contact info@trolltech.com if any conditions of this licensing are
+** not clear to you.
+**
+**********************************************************************/
+
+#ifndef QFILE_DIRECT_H
+#define QFILE_DIRECT_H
+#include <qfile.h>
+#include <qpe/qpcglobal.h>
+
+class QPC_EXPORT QFileDirect : public QFile
+{
+public:
+ QFileDirect() : QFile() { }
+ QFileDirect( const QString &name ) : QFile(name) { }
+
+ FILE *directHandle() { return fh; }
+};
+
+#endif \ No newline at end of file
diff --git a/library/backend/qpcglobal.h b/library/backend/qpcglobal.h
new file mode 100644
index 0000000..0d60272
--- a/dev/null
+++ b/library/backend/qpcglobal.h
@@ -0,0 +1,50 @@
+/**********************************************************************
+** Copyright (C) 2000 Trolltech AS. All rights reserved.
+**
+** This file is part of Qtopia Environment.
+**
+** Licensees holding valid Qtopia Developer license may use this
+** file in accordance with the Qtopia Developer License Agreement
+** provided with the Software.
+**
+** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING
+** THE WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+** PURPOSE.
+**
+** email sales@trolltech.com for information about Qtopia License
+** Agreements.
+**
+** Contact info@trolltech.com if any conditions of this licensing are
+** not clear to you.
+**
+**********************************************************************/
+
+#ifndef QPC_GLOBAL_H
+#define QPC_GLOBAL_H
+
+#if ( defined(Q_OS_WIN32) || defined(Q_OS_WIN64) ) && defined(PALMTOPCENTER)
+#include <qglobal.h>
+// # if defined(QT_NODLL)
+//# undef QPC_MAKEDLL
+//# undef QPC_DLL
+# if defined(QPC_MAKEDLL) /* create a Qt DLL library */
+# if defined(QPC_DLL)
+# undef QPC_DLL
+# endif
+# define QPC_EXPORT __declspec(dllexport)
+# define QPC_TEMPLATEDLL
+# undef QPC_DISABLE_COPY /* avoid unresolved externals */
+# elif defined(QPC_DLL) /* use a Qt DLL library */
+# define QPC_EXPORT __declspec(dllimport)
+# define QPC_TEMPLATEDLL
+# undef QPC_DISABLE_COPY /* avoid unresolved externals */
+# endif
+#else
+# undef QPC_MAKEDLL /* ignore these for other platforms */
+# undef QPC_DLL
+#endif
+#endif
+
+#ifndef QPC_EXPORT
+# define QPC_EXPORT
+#endif
diff --git a/library/backend/recordfields.h b/library/backend/recordfields.h
new file mode 100644
index 0000000..3cddde2
--- a/dev/null
+++ b/library/backend/recordfields.h
@@ -0,0 +1,135 @@
+/**********************************************************************
+** Copyright (C) 2000 Trolltech AS. All rights reserved.
+**
+** This file is part of Qtopia Environment.
+**
+** Licensees holding valid Qtopia Developer license may use this
+** file in accordance with the Qtopia Developer License Agreement
+** provided with the Software.
+**
+** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING
+** THE WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+** PURPOSE.
+**
+** email sales@trolltech.com for information about Qtopia License
+** Agreements.
+**
+** Contact info@trolltech.com if any conditions of this licensing are
+** not clear to you.
+**
+**********************************************************************/
+#ifndef QPC_RECORD_FIELDS_H
+#define QPC_RECORD_FIELDS_H
+#include "qpcglobal.h"
+
+// dataset = "addressbook"
+namespace Qtopia
+{
+ static const int UID_ID = 0;
+ static const int CATEGORY_ID = 1;
+
+ enum AddressBookFields {
+ AddressUid = UID_ID,
+ AddressCategory = CATEGORY_ID,
+
+ Title,
+ FirstName,
+ MiddleName,
+ LastName,
+ Suffix,
+ FileAs,
+
+ // email
+ DefaultEmail,
+ Emails,
+
+ // home
+ HomeStreet,
+ HomeCity,
+ HomeState,
+ HomeZip,
+ HomeCountry,
+ HomePhone,
+ HomeFax,
+ HomeMobile,
+ HomeWebPage,
+
+ // business
+ Company,
+ BusinessStreet,
+ BusinessCity,
+ BusinessState,
+ BusinessZip,
+ BusinessCountry,
+ BusinessWebPage,
+ JobTitle,
+ Department,
+ Office,
+ BusinessPhone,
+ BusinessFax,
+ BusinessMobile,
+ BusinessPager,
+ Profession,
+ Assistant,
+ Manager,
+
+ //personal
+ Spouse,
+ Gender,
+ Birthday,
+ Anniversary,
+ Nickname,
+ Children,
+
+ // other
+ Notes,
+ Groups
+ };
+
+ // dataset = "todolist"
+ enum TaskFields {
+ TaskUid = UID_ID,
+ TaskCategory = CATEGORY_ID,
+
+ HasDate,
+ Completed,
+ TaskDescription,
+ Priority,
+ Date
+ };
+
+ // dataset = "categories" for todos
+ enum CategoryFields {
+ CatUid = UID_ID,
+ CatName,
+ CatAppGroup
+ };
+
+
+// dataset = "datebook"
+ enum DatebookFields {
+ DatebookUid = UID_ID,
+ DatebookCategory = CATEGORY_ID,
+
+ DatebookDescription,
+ Location,
+ TimeZone,
+ Note,
+ StartDateTime,
+ EndDateTime,
+ DatebookType,
+ HasAlarm,
+ SoundType,
+ AlarmTime,
+
+ RepeatPatternType,
+ RepeatPatternFrequency,
+ RepeatPatternPosition,
+ RepeatPatternDays,
+ RepeatPatternHasEndDate,
+ RepeatPatternEndDate,
+ };
+};
+
+
+#endif
diff --git a/library/backend/stringutil.cpp b/library/backend/stringutil.cpp
new file mode 100644
index 0000000..df58f54
--- a/dev/null
+++ b/library/backend/stringutil.cpp
@@ -0,0 +1,415 @@
+/**********************************************************************
+** Copyright (C) 2000 Trolltech AS. All rights reserved.
+**
+** This file is part of Qtopia Environment.
+**
+** This file may be distributed and/or modified under the terms of the
+** GNU General Public License version 2 as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included
+** in the packaging of this file.
+**
+** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING
+** THE WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A
+** PARTICULAR PURPOSE.
+**
+** See http://www.trolltech.com/gpl/ for GPL licensing information.
+**
+** Contact info@trolltech.com if any conditions of this licensing are
+** not clear to you.
+**
+**********************************************************************/
+
+#include "stringutil.h"
+#include <qregexp.h>
+#include <qstringlist.h>
+
+namespace Qtopia
+{
+
+
+
+/*
+ Very, very simple Latin-1 only collation guaranteed to displease anyone
+ who actually uses the non-ASCII characters.
+ */
+
+static const char collationHack[] = {
+0x00, //C-@
+0x01, //C-A
+0x02, //C-B
+0x03, //C-C
+0x04, //C-D
+0x05, //C-E
+0x06, //C-F
+0x07, //C-G
+0x08, //C-H
+0x09, //C-I
+0x0a, //C-J
+0x0b, //C-K
+0x0c, //C-L
+0x0d, //C-M
+0x0e, //C-N
+0x0f, //C-O
+0x10, //C-P
+0x11, //C-Q
+0x12, //C-R
+0x13, //C-S
+0x14, //C-T
+0x15, //C-U
+0x16, //C-V
+0x17, //C-W
+0x18, //C-X
+0x19, //C-Y
+0x1a, //C-Z
+0x1b, //C-[
+0x1c, //C-\
+0x1d, //C-]
+0x1e, //C-^
+0x1f, //C-_
+' ', //
+'!', //!
+'"', //"
+'#', //#
+'$', //$
+'%', //%
+'&', //&
+'\'', //'
+'(', //(
+')', //)
+'*', //*
+'+', //+
+',', //,
+'-', //-
+'.', //.
+'/', ///
+0x80, //0
+0x81, //1
+0x82, //2
+0x83, //3
+0x84, //4
+0x85, //5
+0x86, //6
+0x87, //7
+0x88, //8
+0x89, //9
+':', //:
+';', //;
+'<', //<
+'=', //=
+'>', //>
+'?', //?
+'@', //@
+'A', //A
+'B', //B
+'C', //C
+'D', //D
+'E', //E
+'F', //F
+'G', //G
+'H', //H
+'I', //I
+'J', //J
+'K', //K
+'L', //L
+'M', //M
+'N', //N
+'O', //O
+'P', //P
+'Q', //Q
+'R', //R
+'S', //S
+'T', //T
+'U', //U
+'V', //V
+'W', //W
+'X', //X
+'Y', //Y
+'Z', //Z
+'[', //[
+'\\', //\
+']', //]
+'^', //^
+'_', //_
+'`', //`
+'A', //a
+'B', //b
+'C', //c
+'D', //d
+'E', //e
+'F', //f
+'G', //g
+'H', //h
+'I', //i
+'J', //j
+'K', //k
+'L', //l
+'M', //m
+'N', //n
+'O', //o
+'P', //p
+'Q', //q
+'R', //r
+'S', //s
+'T', //t
+'U', //u
+'V', //v
+'W', //w
+'X', //x
+'Y', //y
+'Z', //z
+'{', //{
+'|', //|
+'}', //}
+'~', //~
+'', //
+0x80, //C-M-@
+0x81, //C-M-A
+0x82, //C-M-B
+0x83, //C-M-C
+0x84, //C-M-D
+0x85, //C-M-E
+0x86, //C-M-F
+0x87, //C-M-G
+0x88, //C-M-H
+0x89, //C-M-I
+0x8a, //C-M-J
+0x8b, //C-M-K
+0x8c, //C-M-L
+0x8d, //C-M-M
+0x8e, //C-M-N
+0x8f, //C-M-O
+0x90, //C-M-P
+0x91, //C-M-Q
+0x92, //C-M-R
+0x93, //C-M-S
+0x94, //C-M-T
+0x95, //C-M-U
+0x96, //C-M-V
+0x97, //C-M-W
+0x98, //C-M-X
+0x99, //C-M-Y
+0x9a, //C-M-Z
+0x9b, //C-M-[
+0x9c, //C-M-\
+0x9d, //C-M-]
+0x9e, //C-M-^
+0x9f, //C-M-_
+' ', // 
+'¡', //¡
+'¢', //¢
+'£', //£
+'¤', //¤
+'¥', //¥
+'¦', //¦
+'§', //§
+'¨', //¨
+'©', //©
+'A', //ª
+'«', //«
+'¬', //¬
+'­', //­
+'®', //®
+'¯', //¯
+'O', //°
+'±', //±
+'²', //²
+'³', //³
+'´', //´
+'µ', //µ
+'P', //¶
+'·', //·
+'¸', //¸
+'¹', //¹
+'O', //º
+'»', //»
+'¼', //¼
+'½', //½
+'¾', //¾
+'¿', //¿
+'A', //À
+'A', //Á
+'A', //Â
+'A', //Ã
+'A', //Ä
+'A', //Å
+'A', //Æ
+'C', //Ç
+'E', //È
+'E', //É
+'E', //Ê
+'E', //Ë
+'I', //Ì
+'I', //Í
+'I', //Î
+'I', //Ï
+'D', //Ð
+'N', //Ñ
+'O', //Ò
+'O', //Ó
+'O', //Ô
+'O', //Õ
+'O', //Ö
+'×', //×
+'O', //Ø
+'U', //Ù
+'U', //Ú
+'U', //Û
+'U', //Ü
+'Y', //Ý
+'T', //Þ
+'S', //ß
+'A', //à
+'A', //á
+'A', //â
+'A', //ã
+'A', //ä
+'A', //å
+'A', //æ
+'C', //ç
+'E', //è
+'E', //é
+'E', //ê
+'E', //ë
+'I', //ì
+'I', //í
+'I', //î
+'I', //ï
+'D', //ð
+'N', //ñ
+'O', //ò
+'O', //ó
+'O', //ô
+'O', //õ
+'O', //ö
+'÷', //÷
+'O', //ø
+'U', //ù
+'U', //ú
+'U', //û
+'U', //ü
+'Y', //ý
+'T', //þ
+'Y', //ÿ
+};
+
+
+
+
+
+static void hackString ( QString &s )
+{
+ int len = s.length();
+ const QChar* uc = s.unicode();
+ for ( int i = 0; i < len; i++ ) {
+ if ( !uc++->row() )
+ s[i] = collationHack[s[i].cell()];
+ }
+}
+
+QString buildSortKey( const QString & s )
+{
+ QString res = s;
+ hackString( res );
+ return res;
+}
+
+QString buildSortKey( const QString & s1, const QString & s2 )
+{
+ QString res = s1 + QChar( '\0' ) + s2;
+ hackString( res );
+ return res;
+}
+
+QString buildSortKey( const QString & s1, const QString & s2,
+ const QString & s3 )
+{
+ QString res = s1 + QChar( '\0' ) + s2 + QChar( '\0' ) + s3;
+ hackString( res );
+ return res;
+}
+
+static inline QChar coll( QChar u )
+{
+ return u.row() ? u : QChar(collationHack[ u.cell() ]);
+}
+
+
+int compare( const QString & s1, const QString & s2 )
+{
+ const QChar* u1 = s1.unicode();
+ const QChar* u2 = s2.unicode();
+
+ if ( u1 == u2 )
+ return 0;
+ if ( u1 == 0 )
+ return 1;
+ if ( u2 == 0 )
+ return -1;
+ int l=QMIN(s1.length(),s2.length());
+ while ( l-- && coll(*u1) == coll(*u2) )
+ u1++,u2++;
+ if ( l==-1 )
+ return ( s1.length()-s2.length() );
+ return u1->unicode() - u2->unicode();
+}
+
+QString simplifyMultiLineSpace( const QString &multiLine )
+{
+ QString result;
+ QStringList lines = QStringList::split("\n", multiLine);
+ for ( QStringList::Iterator it = lines.begin(); it != lines.end(); ++it ) {
+ if ( it != lines.begin() )
+ result += "\n";
+ result += (*it).simplifyWhiteSpace();
+ }
+ return result;
+}
+
+QString escapeString( const QString& plain )
+{
+ QString tmp(plain);
+ int pos = tmp.length();
+ const QChar *uc = plain.unicode();
+ while ( pos-- ) {
+ unsigned char ch = uc[pos].latin1();
+ if ( ch == '&' )
+ tmp.replace( pos, 1, "&amp;" );
+ else if ( ch == '<' )
+ tmp.replace( pos, 1, "&lt;" );
+ else if ( ch == '>' )
+ tmp.replace( pos, 1, "&gt;" );
+ else if ( ch == '\"' )
+ tmp.replace( pos, 1, "&quot;" );
+ }
+ return tmp;
+}
+
+QString plainString( const char* escaped, unsigned int length )
+{
+ return plainString( QString::fromUtf8( escaped, length ) );
+}
+
+QString plainString( const QCString& string )
+{
+ // We first have to pass it through a ::fromUtf8()
+ return plainString( string.data(), string.length() );
+}
+
+QString plainString( const QString& string )
+{
+ QString tmp( string );
+ int pos = -1;
+ while ( (pos = tmp.find( "&", pos +1 ) ) != -1 ) {
+ if ( tmp.find( "&amp;", pos ) == pos )
+ tmp.replace( pos, 5, "&" );
+ else if ( tmp.find( "&lt;", pos ) == pos )
+ tmp.replace( pos, 4, "<" );
+ else if( tmp.find( "&gt;", pos ) == pos )
+ tmp.replace( pos, 4, ">" );
+ else if ( tmp.find( "&quot;", pos ) == pos )
+ tmp.replace( pos, 6, "\"" );
+ }
+ return tmp;
+}
+
+} // namespace QPC
diff --git a/library/backend/stringutil.h b/library/backend/stringutil.h
new file mode 100644
index 0000000..e9daf70
--- a/dev/null
+++ b/library/backend/stringutil.h
@@ -0,0 +1,57 @@
+/**********************************************************************
+** Copyright (C) 2000 Trolltech AS. All rights reserved.
+**
+** This file is part of Qtopia Environment.
+**
+** This file may be distributed and/or modified under the terms of the
+** GNU General Public License version 2 as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included
+** in the packaging of this file.
+**
+** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING
+** THE WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A
+** PARTICULAR PURPOSE.
+**
+** See http://www.trolltech.com/gpl/ for GPL licensing information.
+**
+** Contact info@trolltech.com if any conditions of this licensing are
+** not clear to you.
+**
+**********************************************************************/
+
+
+#ifndef QTPALMTOP_stringutil_h__
+#define QTPALMTOP_stringutil_h__
+
+#include <qstring.h>
+#include "qpcglobal.h"
+
+namespace Qtopia
+{
+
+// Simplifies white space within each line but keeps the new line characters
+QString QPC_EXPORT simplifyMultiLineSpace( const QString &multiLine );
+
+// Creates a QString which doesn't contain any "dangerous"
+// characters (i.e. <, >, &, ")
+QString QPC_EXPORT escapeString( const QString& plain );
+
+// Takes a UTF-8!! string and removes all the XML thingies (entities?)
+// from the string and also calls fromUtf8() on it... so make sure
+// to pass a QCString/const char* with UTF-8 data only
+QString QPC_EXPORT plainString( const char* escaped, unsigned int length );
+QString QPC_EXPORT plainString( const QCString& string );
+
+QString QPC_EXPORT plainString( const QString& string );
+
+
+// collation functions
+int compare( const QString & s1, const QString & s2 );
+QString buildSortKey( const QString & s );
+QString buildSortKey( const QString & s1, const QString & s2 );
+QString buildSortKey( const QString & s1, const QString & s2,
+ const QString & s3 );
+
+}
+
+#endif
diff --git a/library/backend/task.cpp b/library/backend/task.cpp
new file mode 100644
index 0000000..e7d697d
--- a/dev/null
+++ b/library/backend/task.cpp
@@ -0,0 +1,271 @@
+/**********************************************************************
+** Copyright (C) 2001 Trolltech AS. All rights reserved.
+**
+** This file is part of Qtopia Environment.
+**
+** This file may be distributed and/or modified under the terms of the
+** GNU General Public License version 2 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file.
+**
+** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+**
+** See http://www.trolltech.com/gpl/ for GPL licensing information.
+**
+** Contact info@trolltech.com if any conditions of this licensing are
+** not clear to you.
+**
+**********************************************************************/
+
+#include <qpe/task.h>
+#include <qregexp.h>
+#include <qstring.h>
+#include <qpe/recordfields.h>
+#include "vobject_p.h"
+#include "timeconversion.h"
+#include "qfiledirect_p.h"
+
+#include <stdio.h>
+
+using namespace Qtopia;
+UidGen Task::sUidGen( UidGen::Qtopia );
+
+Task::Task() : Record(), mDue( FALSE ),
+mDueDate( QDate::currentDate() ),
+mCompleted( FALSE ), mPriority( 3 ), mDesc()
+{
+}
+
+Task::Task( const QMap<int, QString> &m ) : Record(), mDue( FALSE ),
+mDueDate( QDate::currentDate() ), mCompleted( FALSE ), mPriority( 3 ), mDesc()
+{
+ //qDebug("Task::Task fromMap");
+ //dump( m );
+ for ( QMap<int,QString>::ConstIterator it = m.begin(); it != m.end();++it )
+ switch ( (TaskFields) it.key() ) {
+ case HasDate: if ( *it == "1" ) mDue = TRUE; break;
+ case Completed: setCompleted( *it == "1" ); break;
+ case TaskCategory: setCategories( idsFromString( *it ) ); break;
+ case TaskDescription: setDescription( *it ); break;
+ case Priority: setPriority( (*it).toInt() ); break;
+ case Date: mDueDate = TimeConversion::fromString( (*it) ); break;
+ case TaskUid: setUid( (*it).toInt() ); break;
+ }
+}
+
+Task::~Task()
+{
+}
+
+QMap<int, QString> Task::toMap() const
+{
+ QMap<int, QString> m;
+ m.insert( HasDate, hasDueDate() ? "1" : "0" );
+ m.insert( Completed, isCompleted() ? "1" : "0" );
+ m.insert( TaskCategory, idsToString( categories() ) );
+ m.insert( TaskDescription, description() );
+ m.insert( Priority, QString::number( priority() ) );
+ m.insert( Date, TimeConversion::toString( dueDate() ) );
+ m.insert( TaskUid, QString::number(uid()) );
+
+ //qDebug("Task::toMap");
+ //dump( m );
+ return m;
+}
+
+void Task::save( QString& buf ) const
+{
+ buf += " Completed=\"";
+ // qDebug( "writing %d", complete );
+ buf += QString::number( (int)mCompleted );
+ buf += "\"";
+ buf += " HasDate=\"";
+ // qDebug( "writing %d", );
+ buf += QString::number( (int)mDue );
+ buf += "\"";
+ buf += " Priority=\"";
+ // qDebug ("writing %d", prior );
+ buf += QString::number( mPriority );
+ buf += "\"";
+ buf += " Categories=\"";
+ buf += Qtopia::Record::idsToString( categories() );
+ buf += "\"";
+ buf += " Description=\"";
+ // qDebug( "writing note %s", note.latin1() );
+ buf += Qtopia::escapeString( mDesc );
+ buf += "\"";
+ if ( mDue ) {
+ // qDebug("saving ymd %d %d %d", mDueDate.year(), mDueDate.month(),
+ // mDueDate.day() );
+ buf += " DateYear=\"";
+ buf += QString::number( mDueDate.year() );
+ buf += "\"";
+ buf += " DateMonth=\"";
+ buf += QString::number( mDueDate.month() );
+ buf += "\"";
+ buf += " DateDay=\"";
+ buf += QString::number( mDueDate.day() );
+ buf += "\"";
+ }
+ buf += customToXml();
+ // qDebug ("writing uid %d", uid() );
+ buf += " Uid=\"";
+ buf += QString::number( uid() );
+ // terminate it in the application...
+ buf += "\"";
+}
+
+bool Task::match ( const QRegExp &r ) const
+{
+ // match on priority, description on due date...
+ bool match;
+ match = false;
+ if ( QString::number( mPriority ).find( r ) > -1 )
+ match = true;
+ else if ( mDue && mDueDate.toString().find( r ) > -1 )
+ match = true;
+ else if ( mDesc.find( r ) > -1 )
+ match = true;
+ return match;
+}
+
+static inline VObject *safeAddPropValue( VObject *o, const char *prop, const QString &value )
+{
+ VObject *ret = 0;
+ if ( o && !value.isEmpty() )
+ ret = addPropValue( o, prop, value.latin1() );
+ return ret;
+}
+
+static inline VObject *safeAddProp( VObject *o, const char *prop)
+{
+ VObject *ret = 0;
+ if ( o )
+ ret = addProp( o, prop );
+ return ret;
+}
+
+
+static VObject *createVObject( const Task &t )
+{
+ VObject *vcal = newVObject( VCCalProp );
+ safeAddPropValue( vcal, VCVersionProp, "1.0" );
+ VObject *task = safeAddProp( vcal, VCTodoProp );
+
+ if ( t.hasDueDate() )
+ safeAddPropValue( task, VCDueProp, TimeConversion::toISO8601( t.dueDate() ) );
+ safeAddPropValue( task, VCDescriptionProp, t.description() );
+ if ( t.isCompleted() )
+ safeAddPropValue( task, VCStatusProp, "COMPLETED" );
+ safeAddPropValue( task, VCPriorityProp, QString::number( t.priority() ) );
+
+ return vcal;
+}
+
+
+static Task parseVObject( VObject *obj )
+{
+ Task t;
+
+ VObjectIterator it;
+ initPropIterator( &it, obj );
+ while( moreIteration( &it ) ) {
+ VObject *o = nextVObject( &it );
+ QCString name = vObjectName( o );
+ QCString value = vObjectStringZValue( o );
+ if ( name == VCDueProp ) {
+ t.setDueDate( TimeConversion::fromISO8601( value ).date(), TRUE );
+ }
+ else if ( name == VCDescriptionProp ) {
+ t.setDescription( value );
+ }
+ else if ( name == VCStatusProp ) {
+ if ( value == "COMPLETED" )
+ t.setCompleted( TRUE );
+ }
+ else if ( name == VCPriorityProp ) {
+ t.setPriority( value.toInt() );
+ }
+#if 0
+ else {
+ printf("Name: %s, value=%s\n", name.data(), vObjectStringZValue( o ) );
+ VObjectIterator nit;
+ initPropIterator( &nit, o );
+ while( moreIteration( &nit ) ) {
+ VObject *o = nextVObject( &nit );
+ QCString name = vObjectName( o );
+ QString value = vObjectStringZValue( o );
+ printf(" subprop: %s = %s\n", name.data(), value.latin1() );
+ }
+ }
+#endif
+ }
+
+ return t;
+}
+
+
+
+void Task::writeVCalendar( const QString &filename, const QValueList<Task> &tasks)
+{
+ QFileDirect f( filename.utf8().data() );
+ if ( !f.open( IO_WriteOnly ) ) {
+ qWarning("Unable to open vcard write");
+ return;
+ }
+
+ QValueList<Task>::ConstIterator it;
+ for( it = tasks.begin(); it != tasks.end(); ++it ) {
+ VObject *obj = createVObject( *it );
+ writeVObject(f.directHandle() , obj );
+ cleanVObject( obj );
+ }
+
+ cleanStrTbl();
+}
+
+void Task::writeVCalendar( const QString &filename, const Task &task)
+{
+ QFileDirect f( filename.utf8().data() );
+ if ( !f.open( IO_WriteOnly ) ) {
+ qWarning("Unable to open vcard write");
+ return;
+ }
+
+ VObject *obj = createVObject( task );
+ writeVObject(f.directHandle() , obj );
+ cleanVObject( obj );
+
+ cleanStrTbl();
+}
+
+
+QValueList<Task> Task::readVCalendar( const QString &filename )
+{
+ VObject *obj = Parse_MIME_FromFileName( (char *)filename.utf8().data() );
+
+ QValueList<Task> tasks;
+
+ while ( obj ) {
+ QCString name = vObjectName( obj );
+ if ( name == VCCalProp ) {
+ VObjectIterator nit;
+ initPropIterator( &nit, obj );
+ while( moreIteration( &nit ) ) {
+ VObject *o = nextVObject( &nit );
+ QCString name = vObjectName( o );
+ if ( name == VCTodoProp )
+ tasks.append( parseVObject( o ) );
+ }
+ } else if ( name == VCTodoProp ) {
+ // shouldn't happen, but just to be sure
+ tasks.append( parseVObject( obj ) );
+ }
+ VObject *t = obj;
+ obj = nextVObjectInList(obj);
+ cleanVObject( t );
+ }
+
+ return tasks;
+}
diff --git a/library/backend/task.h b/library/backend/task.h
new file mode 100644
index 0000000..ffe26b0
--- a/dev/null
+++ b/library/backend/task.h
@@ -0,0 +1,77 @@
+/**********************************************************************
+** Copyright (C) 2001 Trolltech AS. All rights reserved.
+**
+** This file is part of Qtopia Environment.
+**
+** This file may be distributed and/or modified under the terms of the
+** GNU General Public License version 2 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file.
+**
+** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+**
+** See http://www.trolltech.com/gpl/ for GPL licensing information.
+**
+** Contact info@trolltech.com if any conditions of this licensing are
+** not clear to you.
+**
+**********************************************************************/
+#ifndef __TASK_H__
+#define __TASK_H__
+
+#include <qpe/palmtoprecord.h>
+#include <qpe/stringutil.h>
+
+#include <qvaluelist.h>
+#include <qdatetime.h>
+
+class TaskPrivate;
+class QPC_EXPORT Task : public Qtopia::Record
+{
+public:
+ Task();
+ Task( const QMap<int, QString> &fromMap );
+ ~Task();
+
+ QMap<int, QString> toMap() const;
+
+ static void writeVCalendar( const QString &filename, const QValueList<Task> &tasks);
+ static void writeVCalendar( const QString &filename, const Task &task);
+ static QValueList<Task> readVCalendar( const QString &filename );
+
+ void setPriority( int priority ) { mPriority = priority; }
+ int priority() const { return mPriority; }
+
+// void setCategory( const QString& category )
+// { mCategory = category.stripWhiteSpace(); }
+// const QString &category() const { return mCategory; }
+
+ void setDescription( const QString& description )
+ { mDesc = Qtopia::simplifyMultiLineSpace(description); }
+ const QString &description() const { return mDesc; }
+
+ void setDueDate( const QDate& date, bool hasDue ) { mDueDate = date; mDue = hasDue; }
+ const QDate &dueDate() const { return mDueDate; }
+ bool hasDueDate() const { return mDue; }
+ void setHasDueDate( bool b ) { mDue = b; }
+
+ void setCompleted( bool b ) { mCompleted = b; }
+ bool isCompleted() const { return mCompleted; }
+
+ void save( QString& buf ) const;
+ bool match( const QRegExp &r ) const;
+
+private:
+ Qtopia::UidGen &uidGen() { return sUidGen; }
+ static Qtopia::UidGen sUidGen;
+
+ bool mDue;
+ QDate mDueDate;
+ bool mCompleted;
+ int mPriority;
+ QString mDesc;
+ TaskPrivate *d;
+};
+
+#endif
diff --git a/library/backend/timeconversion.cpp b/library/backend/timeconversion.cpp
new file mode 100644
index 0000000..a4a2547
--- a/dev/null
+++ b/library/backend/timeconversion.cpp
@@ -0,0 +1,237 @@
+/**********************************************************************
+** Copyright (C) 2000 Trolltech AS. All rights reserved.
+**
+** This file is part of Qtopia Environment.
+**
+** This file may be distributed and/or modified under the terms of the
+** GNU General Public License version 2 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file.
+**
+** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+**
+** See http://www.trolltech.com/gpl/ for GPL licensing information.
+**
+** Contact info@trolltech.com if any conditions of this licensing are
+** not clear to you.
+**
+**********************************************************************/
+
+#include <qglobal.h>
+#include "timeconversion.h"
+#include <qregexp.h>
+#include <stdlib.h>
+
+QString TimeConversion::toString( const QDate &d )
+{
+ QString r = QString::number( d.day() ) + "." +
+ QString::number( d.month() ) + "." +
+ QString::number( d.year() );
+ //qDebug("TimeConversion::toString %s", r.latin1());
+ return r;
+}
+
+QDate TimeConversion::fromString( const QString &datestr )
+{
+ int monthPos = datestr.find('.');
+ int yearPos = datestr.find('.', monthPos+1 );
+ if ( monthPos == -1 || yearPos == -1 ) {
+ qDebug("fromString didn't find . in str = %s; mpos = %d ypos = %d", datestr.latin1(), monthPos, yearPos );
+ return QDate();
+ }
+ int d = datestr.left( monthPos ).toInt();
+ int m = datestr.mid( monthPos+1, yearPos - monthPos - 1 ).toInt();
+ int y = datestr.mid( yearPos+1 ).toInt();
+ QDate date ( y,m,d );
+ //qDebug("TimeConversion::fromString ymd = %s => %d %d %d; mpos = %d ypos = %d", datestr.latin1(), y, m, d, monthPos, yearPos);
+ return date;
+}
+
+time_t TimeConversion::toUTC( const QDateTime& dt )
+{
+ time_t tmp;
+ struct tm *lt;
+
+#if defined(_OS_WIN32) || defined (Q_OS_WIN32) || defined (Q_OS_WIN64)
+ _tzset();
+#else
+ tzset();
+#endif
+
+ // get a tm structure from the system to get the correct tz_name
+ tmp = time( 0 );
+ lt = localtime( &tmp );
+
+ lt->tm_sec = dt.time().second();
+ lt->tm_min = dt.time().minute();
+ lt->tm_hour = dt.time().hour();
+ lt->tm_mday = dt.date().day();
+ lt->tm_mon = dt.date().month() - 1; // 0-11 instead of 1-12
+ lt->tm_year = dt.date().year() - 1900; // year - 1900
+ //lt->tm_wday = dt.date().dayOfWeek(); ignored anyway
+ //lt->tm_yday = dt.date().dayOfYear(); ignored anyway
+ lt->tm_wday = -1;
+ lt->tm_yday = -1;
+ // tm_isdst negative -> mktime will find out about DST
+ lt->tm_isdst = -1;
+ // keep tm_zone and tm_gmtoff
+ tmp = mktime( lt );
+ return tmp;
+}
+
+QDateTime TimeConversion::fromUTC( time_t time )
+{
+ struct tm *lt;
+
+#if defined(_OS_WIN32) || defined (Q_OS_WIN32) || defined (Q_OS_WIN64)
+ _tzset();
+#else
+ tzset();
+#endif
+ lt = localtime( &time );
+ QDateTime dt;
+ dt.setDate( QDate( lt->tm_year + 1900, lt->tm_mon + 1, lt->tm_mday ) );
+ dt.setTime( QTime( lt->tm_hour, lt->tm_min, lt->tm_sec ) );
+ return dt;
+}
+
+
+int TimeConversion::secsTo( const QDateTime &from, const QDateTime &to )
+{
+ return toUTC( to ) - toUTC( from );
+}
+
+QCString TimeConversion::toISO8601( const QDate &d )
+{
+ time_t tmp = toUTC( d );
+ struct tm *utc = gmtime( &tmp );
+
+ QCString str;
+ str.sprintf("%04d%02d%02d", (utc->tm_year + 1900), utc->tm_mon+1, utc->tm_mday );
+ return str;
+}
+
+QCString TimeConversion::toISO8601( const QDateTime &dt )
+{
+ time_t tmp = toUTC( dt );
+ struct tm *utc = gmtime( &tmp );
+
+ QCString str;
+ str.sprintf("%04d%02d%02dT%02d%02d%02dZ",
+ (utc->tm_year + 1900), utc->tm_mon+1, utc->tm_mday,
+ utc->tm_hour, utc->tm_min, utc->tm_sec );
+ return str;
+}
+
+QDateTime TimeConversion::fromISO8601( const QCString &s )
+{
+
+#if defined(_OS_WIN32) || defined (Q_OS_WIN32) || defined (Q_OS_WIN64)
+ _tzset();
+#else
+ tzset();
+#endif
+
+ struct tm *thetime = new tm;
+
+ QCString str = s.copy();
+ str.replace(QRegExp("-"), "" );
+ str.replace(QRegExp(":"), "" );
+ str.stripWhiteSpace();
+ str = str.lower();
+
+ int i = str.find( "t" );
+ QCString date;
+ QCString timestr;
+ if ( i != -1 ) {
+ date = str.left( i );
+ timestr = str.mid( i+1 );
+ } else {
+ date = str;
+ }
+
+// qDebug("--- parsing ISO time---");
+ thetime->tm_year = 100;
+ thetime->tm_mon = 0;
+ thetime->tm_mday = 0;
+ thetime->tm_hour = 0;
+ thetime->tm_min = 0;
+ thetime->tm_sec = 0;
+
+// qDebug("date = %s", date.data() );
+
+ switch( date.length() ) {
+ case 8:
+ thetime->tm_mday = date.right( 2 ).toInt();
+ case 6:
+ thetime->tm_mon = date.mid( 4, 2 ).toInt() - 1;
+ case 4:
+ thetime->tm_year = date.left( 4 ).toInt();
+ thetime->tm_year -= 1900;
+ break;
+ default:
+ break;
+ }
+
+ int tzoff = 0;
+ bool inLocalTime = FALSE;
+ if ( timestr.find( 'z' ) == (int)timestr.length() - 1 )
+ // UTC
+ timestr = timestr.left( timestr.length() -1 );
+ else {
+ int plus = timestr.find( "+" );
+ int minus = timestr.find( "-" );
+ if ( plus != -1 || minus != -1 ) {
+ // have a timezone offset
+ plus = (plus != -1) ? plus : minus;
+ QCString off = timestr.mid( plus );
+ timestr = timestr.left( plus );
+
+ int tzoffhour = 0;
+ int tzoffmin = 0;
+ switch( off.length() ) {
+ case 5:
+ tzoffmin = off.mid(3).toInt();
+ case 3:
+ tzoffhour = off.left(3).toInt();
+ default:
+ break;
+ }
+ tzoff = 60*tzoffhour + tzoffmin;
+ } else
+ inLocalTime = TRUE;
+ }
+
+ // get the time:
+ switch( timestr.length() ) {
+ case 6:
+ thetime->tm_sec = timestr.mid( 4 ).toInt();
+ case 4:
+ thetime->tm_min = timestr.mid( 2, 2 ).toInt();
+ case 2:
+ thetime->tm_hour = timestr.left( 2 ).toInt();
+ default:
+ break;
+ }
+
+ int tzloc = 0;
+ time_t tmp = time( 0 );
+ if ( !inLocalTime ) {
+ // have to get the offset between gmt and local time
+ struct tm *lt = localtime( &tmp );
+ tzloc = mktime( lt );
+ struct tm *ut = gmtime( &tmp );
+ tzloc -= mktime( ut );
+ }
+// qDebug("time: %d %d %d, tzloc=%d, tzoff=%d", thetime->tm_hour, thetime->tm_min, thetime->tm_sec,
+// tzloc, tzoff );
+
+ tmp = mktime( thetime );
+ tmp += 60*(-tzloc + tzoff);
+
+ delete thetime;
+
+ return fromUTC( tmp );
+}
+
diff --git a/library/backend/timeconversion.h b/library/backend/timeconversion.h
new file mode 100644
index 0000000..1724812
--- a/dev/null
+++ b/library/backend/timeconversion.h
@@ -0,0 +1,45 @@
+/**********************************************************************
+** Copyright (C) 2000 Trolltech AS. All rights reserved.
+**
+** This file is part of Qtopia Environment.
+**
+** This file may be distributed and/or modified under the terms of the
+** GNU General Public License version 2 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file.
+**
+** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+**
+** See http://www.trolltech.com/gpl/ for GPL licensing information.
+**
+** Contact info@trolltech.com if any conditions of this licensing are
+** not clear to you.
+**
+**********************************************************************/
+
+#ifndef __timeconversion_h__
+#define __timeconversion_h__
+
+#include <time.h>
+#include <sys/types.h>
+#include <qdatetime.h>
+
+#include <qpe/qpcglobal.h>
+
+class QPC_EXPORT TimeConversion
+{
+public:
+ static QString toString( const QDate &d );
+ static QDate fromString( const QString &datestr );
+
+ static time_t toUTC( const QDateTime& dt );
+ static QDateTime fromUTC( time_t time );
+ static int secsTo( const QDateTime &from, const QDateTime &to );
+
+ static QCString toISO8601( const QDate & );
+ static QCString toISO8601( const QDateTime & );
+ static QDateTime fromISO8601( const QCString & );
+};
+
+#endif // __timeconversion_h__
diff --git a/library/backend/vcc.y b/library/backend/vcc.y
new file mode 100644
index 0000000..0225982
--- a/dev/null
+++ b/library/backend/vcc.y
@@ -0,0 +1,1199 @@
+%{
+
+/***************************************************************************
+(C) Copyright 1996 Apple Computer, Inc., AT&T Corp., International
+Business Machines Corporation and Siemens Rolm Communications Inc.
+
+For purposes of this license notice, the term Licensors shall mean,
+collectively, Apple Computer, Inc., AT&T Corp., International
+Business Machines Corporation and Siemens Rolm Communications Inc.
+The term Licensor shall mean any of the Licensors.
+
+Subject to acceptance of the following conditions, permission is hereby
+granted by Licensors without the need for written agreement and without
+license or royalty fees, to use, copy, modify and distribute this
+software for any purpose.
+
+The above copyright notice and the following four paragraphs must be
+reproduced in all copies of this software and any software including
+this software.
+
+THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS AND NO LICENSOR SHALL HAVE
+ANY OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS OR
+MODIFICATIONS.
+
+IN NO EVENT SHALL ANY LICENSOR BE LIABLE TO ANY PARTY FOR DIRECT,
+INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT
+OF THE USE OF THIS SOFTWARE EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGE.
+
+EACH LICENSOR SPECIFICALLY DISCLAIMS ANY WARRANTIES, EXPRESS OR IMPLIED,
+INCLUDING BUT NOT LIMITED TO ANY WARRANTY OF NONINFRINGEMENT OR THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.
+
+The software is provided with RESTRICTED RIGHTS. Use, duplication, or
+disclosure by the government are subject to restrictions set forth in
+DFARS 252.227-7013 or 48 CFR 52.227-19, as applicable.
+
+***************************************************************************/
+
+/*
+ * src: vcc.c
+ * doc: Parser for vCard and vCalendar. Note that this code is
+ * generated by a yacc parser generator. Generally it should not
+ * be edited by hand. The real source is vcc.y. The #line directives
+ * can be commented out here to make it easier to trace through
+ * in a debugger. However, if a bug is found it should
+ * be fixed in vcc.y and this file regenerated.
+ */
+
+
+/* debugging utilities */
+#if __DEBUG
+#define DBG_(x) printf x
+#else
+#define DBG_(x)
+#endif
+
+/**** External Functions ****/
+
+/* assign local name to parser variables and functions so that
+ we can use more than one yacc based parser.
+*/
+
+#if 0
+#define yyparse mime_parse
+#define yylex mime_lex
+#define yyerror mime_error
+#define yychar mime_char
+/* #define p_yyval p_mime_val */
+#undef yyval
+#define yyval mime_yyval
+/* #define p_yylval p_mime_lval */
+#undef yylval
+#define yylval mime_yylval
+#define yydebug mime_debug
+#define yynerrs mime_nerrs
+#define yyerrflag mime_errflag
+#define yyss mime_ss
+#define yyssp mime_ssp
+#define yyvs mime_vs
+#define yyvsp mime_vsp
+#define yylhs mime_lhs
+#define yylen mime_len
+#define yydefred mime_defred
+#define yydgoto mime_dgoto
+#define yysindex mime_sindex
+#define yyrindex mime_rindex
+#define yygindex mime_gindex
+#define yytable mime_table
+#define yycheck mime_check
+#define yyname mime_name
+#define yyrule mime_rule
+#ifdef YYPREFIX
+#undef YYPREFIX
+#endif
+#define YYPREFIX "mime_"
+#endif
+
+
+#ifndef _NO_LINE_FOLDING
+#define _SUPPORT_LINE_FOLDING 1
+#endif
+
+/* undef below if compile with MFC */
+/* #define INCLUDEMFC 1 */
+
+#if defined(WIN32) || defined(_WIN32)
+#ifdef INCLUDEMFC
+#include <afx.h>
+#endif
+#endif
+
+#include <string.h>
+#ifndef __MWERKS__
+#include <stdlib.h>
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+
+//#ifdef PALMTOPCENTER
+//#include <qpe/vobject_p.h>
+//#else
+#include "vobject_p.h"
+//#endif
+
+/**** Types, Constants ****/
+
+#define YYDEBUG 0 /* 1 to compile in some debugging code */
+#define MAXTOKEN 256 /* maximum token (line) length */
+#define YYSTACKSIZE 100 // ~unref ?
+#define MAXLEVEL 10 /* max # of nested objects parseable */
+ /* (includes outermost) */
+
+
+/**** Global Variables ****/
+int mime_lineNum, mime_numErrors; /* yyerror() can use these */
+static VObject* vObjList;
+static VObject *curProp;
+static VObject *curObj;
+static VObject* ObjStack[MAXLEVEL];
+static int ObjStackTop;
+
+
+/* A helpful utility for the rest of the app. */
+#if __CPLUSPLUS__
+extern "C" {
+#endif
+
+ extern void yyerror(char *s);
+
+#if __CPLUSPLUS__
+ };
+#endif
+
+int yyparse();
+
+enum LexMode {
+ L_NORMAL,
+ L_VCARD,
+ L_VCAL,
+ L_VEVENT,
+ L_VTODO,
+ L_VALUES,
+ L_BASE64,
+ L_QUOTED_PRINTABLE
+ };
+
+/**** Private Forward Declarations ****/
+static int pushVObject(const char *prop);
+static VObject* popVObject();
+static void lexPopMode(int top);
+static int lexWithinMode(enum LexMode mode);
+static void lexPushMode(enum LexMode mode);
+static void enterProps(const char *s);
+static void enterAttr(const char *s1, const char *s2);
+static void enterValues(const char *value);
+#define mime_error yyerror
+void mime_error(char *s);
+void mime_error_(char *s);
+
+%}
+
+/***************************************************************************/
+/*** The grammar ****/
+/***************************************************************************/
+
+%union {
+ char *str;
+ VObject *vobj;
+ }
+
+%token
+ EQ COLON DOT SEMICOLON SPACE HTAB LINESEP NEWLINE
+ BEGIN_VCARD END_VCARD BEGIN_VCAL END_VCAL
+ BEGIN_VEVENT END_VEVENT BEGIN_VTODO END_VTODO
+ ID
+
+/*
+ * NEWLINE is the token that would occur outside a vCard,
+ * while LINESEP is the token that would occur inside a vCard.
+ */
+
+%token <str>
+ STRING ID
+
+%type <str> name value
+
+%type <vobj> vcard vcal vobject
+
+%start mime
+
+%%
+
+
+mime: vobjects
+ ;
+
+vobjects: vobjects vobject
+ { addList(&vObjList, $2); curObj = 0; }
+ | vobject
+ { addList(&vObjList, $1); curObj = 0; }
+ ;
+
+vobject: vcard
+ | vcal
+ ;
+
+vcard:
+ BEGIN_VCARD
+ {
+ lexPushMode(L_VCARD);
+ if (!pushVObject(VCCardProp)) YYERROR;
+ }
+ items END_VCARD
+ {
+ lexPopMode(0);
+ $$ = popVObject();
+ }
+ | BEGIN_VCARD
+ {
+ lexPushMode(L_VCARD);
+ if (!pushVObject(VCCardProp)) YYERROR;
+ }
+ END_VCARD
+ {
+ lexPopMode(0);
+ $$ = popVObject();
+ }
+ ;
+
+items: items item
+ | item
+ ;
+
+item: prop COLON
+ {
+ lexPushMode(L_VALUES);
+ }
+ values LINESEP
+ {
+ if (lexWithinMode(L_BASE64) || lexWithinMode(L_QUOTED_PRINTABLE))
+ lexPopMode(0);
+ lexPopMode(0);
+ }
+ | error
+ ;
+
+prop: name
+ {
+ enterProps($1);
+ }
+ attr_params
+ | name
+ {
+ enterProps($1);
+ }
+ ;
+
+attr_params: attr_params attr_param
+ | attr_param
+ ;
+
+attr_param: SEMICOLON attr
+ ;
+
+attr: name
+ {
+ enterAttr($1,0);
+ }
+ | name EQ name
+ {
+ enterAttr($1,$3);
+
+ }
+ ;
+
+name: ID
+ ;
+
+values: value SEMICOLON { enterValues($1); } values
+ | value
+ { enterValues($1); }
+ ;
+
+value: STRING
+ |
+ { $$ = 0; }
+ ;
+
+vcal:
+ BEGIN_VCAL
+ { if (!pushVObject(VCCalProp)) YYERROR; }
+ calitems
+ END_VCAL
+ { $$ = popVObject(); }
+ | BEGIN_VCAL
+ { if (!pushVObject(VCCalProp)) YYERROR; }
+ END_VCAL
+ { $$ = popVObject(); }
+ ;
+
+calitems: calitems calitem
+ | calitem
+ ;
+
+calitem:
+ eventitem
+ | todoitem
+ | items
+ ;
+
+eventitem:
+ BEGIN_VEVENT
+ {
+ lexPushMode(L_VEVENT);
+ if (!pushVObject(VCEventProp)) YYERROR;
+ }
+ items
+ END_VEVENT
+ {
+ lexPopMode(0);
+ popVObject();
+ }
+ | BEGIN_VEVENT
+ {
+ lexPushMode(L_VEVENT);
+ if (!pushVObject(VCEventProp)) YYERROR;
+ }
+ END_VEVENT
+ {
+ lexPopMode(0);
+ popVObject();
+ }
+ ;
+
+todoitem:
+ BEGIN_VTODO
+ {
+ lexPushMode(L_VTODO);
+ if (!pushVObject(VCTodoProp)) YYERROR;
+ }
+ items
+ END_VTODO
+ {
+ lexPopMode(0);
+ popVObject();
+ }
+ | BEGIN_VTODO
+ {
+ lexPushMode(L_VTODO);
+ if (!pushVObject(VCTodoProp)) YYERROR;
+ }
+ END_VTODO
+ {
+ lexPopMode(0);
+ popVObject();
+ }
+ ;
+
+%%
+/*------------------------------------*/
+static int pushVObject(const char *prop)
+ {
+ VObject *newObj;
+ if (ObjStackTop == MAXLEVEL)
+ return FALSE;
+
+ ObjStack[++ObjStackTop] = curObj;
+
+ if (curObj) {
+ newObj = addProp(curObj,prop);
+ curObj = newObj;
+ }
+ else
+ curObj = newVObject(prop);
+
+ return TRUE;
+ }
+
+
+/*---------------------------------------*/
+/* This pops the recently built vCard off the stack and returns it. */
+static VObject* popVObject()
+ {
+ VObject *oldObj;
+ if (ObjStackTop < 0) {
+ yyerror("pop on empty Object Stack\n");
+ return 0;
+ }
+ oldObj = curObj;
+ curObj = ObjStack[ObjStackTop--];
+
+ return oldObj;
+ }
+
+
+static void enterValues(const char *value)
+ {
+ if (fieldedProp && *fieldedProp) {
+ if (value) {
+ addPropValue(curProp,*fieldedProp,value);
+ }
+ /* else this field is empty, advance to next field */
+ fieldedProp++;
+ }
+ else {
+ if (value) {
+ setVObjectStringZValue_(curProp,strdup( value ));
+ }
+ }
+ deleteStr(value);
+ }
+
+static void enterProps(const char *s)
+ {
+ curProp = addGroup(curObj,s);
+ deleteStr(s);
+ }
+
+static void enterAttr(const char *s1, const char *s2)
+ {
+ const char *p1, *p2;
+ p1 = lookupProp_(s1);
+ if (s2) {
+ VObject *a;
+ p2 = lookupProp_(s2);
+ a = addProp(curProp,p1);
+ setVObjectStringZValue(a,p2);
+ }
+ else
+ addProp(curProp,p1);
+ if (qstricmp(p1,VCBase64Prop) == 0 || (s2 && qstricmp(p2,VCBase64Prop)==0))
+ lexPushMode(L_BASE64);
+ else if (qstricmp(p1,VCQuotedPrintableProp) == 0
+ || (s2 && qstricmp(p2,VCQuotedPrintableProp)==0))
+ lexPushMode(L_QUOTED_PRINTABLE);
+ deleteStr(s1); deleteStr(s2);
+ }
+
+
+#define MAX_LEX_LOOKAHEAD_0 32
+#define MAX_LEX_LOOKAHEAD 64
+#define MAX_LEX_MODE_STACK_SIZE 10
+#define LEXMODE() (lexBuf.lexModeStack[lexBuf.lexModeStackTop])
+
+struct LexBuf {
+ /* input */
+#ifdef INCLUDEMFC
+ CFile *inputFile;
+#else
+ FILE *inputFile;
+#endif
+ char *inputString;
+ unsigned long curPos;
+ unsigned long inputLen;
+ /* lookahead buffer */
+ /* -- lookahead buffer is short instead of char so that EOF
+ / can be represented correctly.
+ */
+ unsigned long len;
+ short buf[MAX_LEX_LOOKAHEAD];
+ unsigned long getPtr;
+ /* context stack */
+ unsigned long lexModeStackTop;
+ enum LexMode lexModeStack[MAX_LEX_MODE_STACK_SIZE];
+ /* token buffer */
+ unsigned long maxToken;
+ char *strs;
+ unsigned long strsLen;
+ } lexBuf;
+
+static void lexPushMode(enum LexMode mode)
+ {
+ if (lexBuf.lexModeStackTop == (MAX_LEX_MODE_STACK_SIZE-1))
+ yyerror("lexical context stack overflow");
+ else {
+ lexBuf.lexModeStack[++lexBuf.lexModeStackTop] = mode;
+ }
+ }
+
+static void lexPopMode(int top)
+ {
+ /* special case of pop for ease of error recovery -- this
+ version will never underflow */
+ if (top)
+ lexBuf.lexModeStackTop = 0;
+ else
+ if (lexBuf.lexModeStackTop > 0) lexBuf.lexModeStackTop--;
+ }
+
+static int lexWithinMode(enum LexMode mode) {
+ unsigned long i;
+ for (i=0;i<lexBuf.lexModeStackTop;i++)
+ if (mode == lexBuf.lexModeStack[i]) return 1;
+ return 0;
+ }
+
+static char lexGetc_()
+ {
+ /* get next char from input, no buffering. */
+ if (lexBuf.curPos == lexBuf.inputLen)
+ return EOF;
+ else if (lexBuf.inputString)
+ return *(lexBuf.inputString + lexBuf.curPos++);
+ else {
+#ifdef INCLUDEMFC
+ char result;
+ return lexBuf.inputFile->Read(&result, 1) == 1 ? result : EOF;
+#else
+ return fgetc(lexBuf.inputFile);
+#endif
+ }
+ }
+
+static int lexGeta()
+ {
+ ++lexBuf.len;
+ return (lexBuf.buf[lexBuf.getPtr] = lexGetc_());
+ }
+
+static int lexGeta_(int i)
+ {
+ ++lexBuf.len;
+ return (lexBuf.buf[(lexBuf.getPtr+i)%MAX_LEX_LOOKAHEAD] = lexGetc_());
+ }
+
+static void lexSkipLookahead() {
+ if (lexBuf.len > 0 && lexBuf.buf[lexBuf.getPtr]!=EOF) {
+ /* don't skip EOF. */
+ lexBuf.getPtr = (lexBuf.getPtr + 1) % MAX_LEX_LOOKAHEAD;
+ lexBuf.len--;
+ }
+ }
+
+static int lexLookahead() {
+ int c = (lexBuf.len)?
+ lexBuf.buf[lexBuf.getPtr]:
+ lexGeta();
+ /* do the \r\n -> \n or \r -> \n translation here */
+ if (c == '\r') {
+ int a = (lexBuf.len>1)?
+ lexBuf.buf[(lexBuf.getPtr+1)%MAX_LEX_LOOKAHEAD]:
+ lexGeta_(1);
+ if (a == '\n') {
+ lexSkipLookahead();
+ }
+ lexBuf.buf[lexBuf.getPtr] = c = '\n';
+ }
+ else if (c == '\n') {
+ int a = (lexBuf.len>1)?
+ lexBuf.buf[lexBuf.getPtr+1]:
+ lexGeta_(1);
+ if (a == '\r') {
+ lexSkipLookahead();
+ }
+ lexBuf.buf[lexBuf.getPtr] = '\n';
+ }
+ return c;
+ }
+
+static int lexGetc() {
+ int c = lexLookahead();
+ if (lexBuf.len > 0 && lexBuf.buf[lexBuf.getPtr]!=EOF) {
+ /* EOF will remain in lookahead buffer */
+ lexBuf.getPtr = (lexBuf.getPtr + 1) % MAX_LEX_LOOKAHEAD;
+ lexBuf.len--;
+ }
+ return c;
+ }
+
+static void lexSkipLookaheadWord() {
+ if (lexBuf.strsLen <= lexBuf.len) {
+ lexBuf.len -= lexBuf.strsLen;
+ lexBuf.getPtr = (lexBuf.getPtr + lexBuf.strsLen) % MAX_LEX_LOOKAHEAD;
+ }
+ }
+
+static void lexClearToken()
+ {
+ lexBuf.strsLen = 0;
+ }
+
+static void lexAppendc(int c)
+ {
+ lexBuf.strs[lexBuf.strsLen] = c;
+ /* append up to zero termination */
+ if (c == 0) return;
+ lexBuf.strsLen++;
+ if (lexBuf.strsLen > lexBuf.maxToken) {
+ /* double the token string size */
+ lexBuf.maxToken <<= 1;
+ lexBuf.strs = (char*) realloc(lexBuf.strs,(size_t)lexBuf.maxToken);
+ }
+ }
+
+static char* lexStr() {
+ return dupStr(lexBuf.strs,(size_t)lexBuf.strsLen+1);
+ }
+
+static void lexSkipWhite() {
+ int c = lexLookahead();
+ while (c == ' ' || c == '\t') {
+ lexSkipLookahead();
+ c = lexLookahead();
+ }
+ }
+
+static char* lexGetWord() {
+ int c;
+ lexSkipWhite();
+ lexClearToken();
+ c = lexLookahead();
+ while (c != EOF && !strchr("\t\n ;:=",c)) {
+ lexAppendc(c);
+ lexSkipLookahead();
+ c = lexLookahead();
+ }
+ lexAppendc(0);
+ return lexStr();
+ }
+
+static void lexPushLookaheadc(int c) {
+ int putptr;
+ /* can't putback EOF, because it never leaves lookahead buffer */
+ if (c == EOF) return;
+ putptr = (int)lexBuf.getPtr - 1;
+ if (putptr < 0) putptr += MAX_LEX_LOOKAHEAD;
+ lexBuf.getPtr = putptr;
+ lexBuf.buf[putptr] = c;
+ lexBuf.len += 1;
+ }
+
+static char* lexLookaheadWord() {
+ /* this function can lookahead word with max size of MAX_LEX_LOOKAHEAD_0
+ / and thing bigger than that will stop the lookahead and return 0;
+ / leading white spaces are not recoverable.
+ */
+ int c;
+ int len = 0;
+ int curgetptr = 0;
+ lexSkipWhite();
+ lexClearToken();
+ curgetptr = (int)lexBuf.getPtr; // remember!
+ while (len < (MAX_LEX_LOOKAHEAD_0)) {
+ c = lexGetc();
+ len++;
+ if (c == EOF || strchr("\t\n ;:=", c)) {
+ lexAppendc(0);
+ /* restore lookahead buf. */
+ lexBuf.len += len;
+ lexBuf.getPtr = curgetptr;
+ return lexStr();
+ }
+ else
+ lexAppendc(c);
+ }
+ lexBuf.len += len; /* char that has been moved to lookahead buffer */
+ lexBuf.getPtr = curgetptr;
+ return 0;
+ }
+
+#ifdef _SUPPORT_LINE_FOLDING
+static void handleMoreRFC822LineBreak(int c) {
+ /* suport RFC 822 line break in cases like
+ * ADR: foo;
+ * morefoo;
+ * more foo;
+ */
+ if (c == ';') {
+ int a;
+ lexSkipLookahead();
+ /* skip white spaces */
+ a = lexLookahead();
+ while (a == ' ' || a == '\t') {
+ lexSkipLookahead();
+ a = lexLookahead();
+ }
+ if (a == '\n') {
+ lexSkipLookahead();
+ a = lexLookahead();
+ if (a == ' ' || a == '\t') {
+ /* continuation, throw away all the \n and spaces read so
+ * far
+ */
+ lexSkipWhite();
+ lexPushLookaheadc(';');
+ }
+ else {
+ lexPushLookaheadc('\n');
+ lexPushLookaheadc(';');
+ }
+ }
+ else {
+ lexPushLookaheadc(';');
+ }
+ }
+ }
+
+static char* lexGet1Value() {
+ int c;
+ lexSkipWhite();
+ c = lexLookahead();
+ lexClearToken();
+ while (c != EOF && c != ';') {
+ if (c == '\\' ) {
+ int a;
+ lexSkipLookahead();
+ a = lexLookahead();
+ if ( a != ';' ) {
+ lexAppendc('\\');
+ } else {
+ lexAppendc( ';' );
+ lexSkipLookahead();
+ }
+ } else if (c == '\n') {
+ int a;
+ lexSkipLookahead();
+ a = lexLookahead();
+ if (a == ' ' || a == '\t') {
+ lexAppendc(' ');
+ lexSkipLookahead();
+ }
+ else {
+ lexPushLookaheadc('\n');
+ break;
+ }
+ }
+ else {
+ lexAppendc(c);
+ lexSkipLookahead();
+ }
+ c = lexLookahead();
+ }
+ lexAppendc(0);
+ handleMoreRFC822LineBreak(c);
+ return c==EOF?0:lexStr();
+ }
+#endif
+
+static int match_begin_name(int end) {
+ char *n = lexLookaheadWord();
+ int token = ID;
+ if (n) {
+ if (!qstricmp(n,"vcard")) token = end?END_VCARD:BEGIN_VCARD;
+ else if (!qstricmp(n,"vcalendar")) token = end?END_VCAL:BEGIN_VCAL;
+ else if (!qstricmp(n,"vevent")) token = end?END_VEVENT:BEGIN_VEVENT;
+ else if (!qstricmp(n,"vtodo")) token = end?END_VTODO:BEGIN_VTODO;
+ deleteStr(n);
+ return token;
+ }
+ return 0;
+ }
+
+
+#ifdef INCLUDEMFC
+void initLex(const char *inputstring, unsigned long inputlen, CFile *inputfile)
+#else
+void initLex(const char *inputstring, unsigned long inputlen, FILE *inputfile)
+#endif
+ {
+ // initialize lex mode stack
+ lexBuf.lexModeStack[lexBuf.lexModeStackTop=0] = L_NORMAL;
+
+ // iniatialize lex buffer.
+ lexBuf.inputString = (char*) inputstring;
+ lexBuf.inputLen = inputlen;
+ lexBuf.curPos = 0;
+ lexBuf.inputFile = inputfile;
+
+ lexBuf.len = 0;
+ lexBuf.getPtr = 0;
+
+ lexBuf.maxToken = MAXTOKEN;
+ lexBuf.strs = (char*)malloc(MAXTOKEN);
+ lexBuf.strsLen = 0;
+
+ }
+
+static void finiLex() {
+ free(lexBuf.strs);
+ }
+
+
+/*-----------------------------------*/
+/* This parses and converts the base64 format for binary encoding into
+ * a decoded buffer (allocated with new). See RFC 1521.
+ */
+static char * lexGetDataFromBase64()
+ {
+ unsigned long bytesLen = 0, bytesMax = 0;
+ int quadIx = 0, pad = 0;
+ unsigned long trip = 0;
+ unsigned char b;
+ int c;
+ unsigned char *bytes = NULL;
+ unsigned char *oldBytes = NULL;
+
+ DBG_(("db: lexGetDataFromBase64\n"));
+ while (1) {
+ c = lexGetc();
+ if (c == '\n') {
+ ++mime_lineNum;
+ if (lexLookahead() == '\n') {
+ /* a '\n' character by itself means end of data */
+ break;
+ }
+ else continue; /* ignore '\n' */
+ }
+ else {
+ if ((c >= 'A') && (c <= 'Z'))
+ b = (unsigned char)(c - 'A');
+ else if ((c >= 'a') && (c <= 'z'))
+ b = (unsigned char)(c - 'a') + 26;
+ else if ((c >= '0') && (c <= '9'))
+ b = (unsigned char)(c - '0') + 52;
+ else if (c == '+')
+ b = 62;
+ else if (c == '/')
+ b = 63;
+ else if (c == '=') {
+ b = 0;
+ pad++;
+ } else if ((c == ' ') || (c == '\t')) {
+ continue;
+ } else { /* error condition */
+ if (bytes) free(bytes);
+ else if (oldBytes) free(oldBytes);
+ // error recovery: skip until 2 adjacent newlines.
+ DBG_(("db: invalid character 0x%x '%c'\n", c,c));
+ if (c != EOF) {
+ c = lexGetc();
+ while (c != EOF) {
+ if (c == '\n' && lexLookahead() == '\n') {
+ ++mime_lineNum;
+ break;
+ }
+ c = lexGetc();
+ }
+ }
+ return NULL;
+ }
+ trip = (trip << 6) | b;
+ if (++quadIx == 4) {
+ unsigned char outBytes[3];
+ int numOut;
+ int i;
+ for (i = 0; i < 3; i++) {
+ outBytes[2-i] = (unsigned char)(trip & 0xFF);
+ trip >>= 8;
+ }
+ numOut = 3 - pad;
+ if (bytesLen + numOut > bytesMax) {
+ if (!bytes) {
+ bytesMax = 1024;
+ bytes = (unsigned char*)malloc((size_t)bytesMax);
+ }
+ else {
+ bytesMax <<= 2;
+ oldBytes = bytes;
+ bytes = (unsigned char*)realloc(bytes,(size_t)bytesMax);
+ }
+ if (bytes == 0) {
+ mime_error("out of memory while processing BASE64 data\n");
+ }
+ }
+ if (bytes) {
+ memcpy(bytes + bytesLen, outBytes, numOut);
+ bytesLen += numOut;
+ }
+ trip = 0;
+ quadIx = 0;
+ }
+ }
+ } /* while */
+ DBG_(("db: bytesLen = %d\n", bytesLen));
+ /* kludge: all this won't be necessary if we have tree form
+ representation */
+ if (bytes) {
+ setValueWithSize(curProp,bytes,(unsigned int)bytesLen);
+ free(bytes);
+ }
+ else if (oldBytes) {
+ setValueWithSize(curProp,oldBytes,(unsigned int)bytesLen);
+ free(oldBytes);
+ }
+ return 0;
+ }
+
+static int match_begin_end_name(int end) {
+ int token;
+ lexSkipWhite();
+ if (lexLookahead() != ':') return ID;
+ lexSkipLookahead();
+ lexSkipWhite();
+ token = match_begin_name(end);
+ if (token == ID) {
+ lexPushLookaheadc(':');
+ DBG_(("db: ID '%s'\n", yylval.str));
+ return ID;
+ }
+ else if (token != 0) {
+ lexSkipLookaheadWord();
+ deleteStr(yylval.str);
+ DBG_(("db: begin/end %d\n", token));
+ return token;
+ }
+ return 0;
+ }
+
+static char* lexGetQuotedPrintable()
+ {
+ char cur;
+
+ lexClearToken();
+ do {
+ cur = lexGetc();
+ switch (cur) {
+ case '=': {
+ int c = 0;
+ int next[2];
+ int i;
+ for (i = 0; i < 2; i++) {
+ next[i] = lexGetc();
+ if (next[i] >= '0' && next[i] <= '9')
+ c = c * 16 + next[i] - '0';
+ else if (next[i] >= 'A' && next[i] <= 'F')
+ c = c * 16 + next[i] - 'A' + 10;
+ else
+ break;
+ }
+ if (i == 0) {
+ /* single '=' follow by LINESEP is continuation sign? */
+ if (next[0] == '\n') {
+ ++mime_lineNum;
+ }
+ else {
+ lexPushLookaheadc('=');
+ goto EndString;
+ }
+ }
+ else if (i == 1) {
+ lexPushLookaheadc(next[1]);
+ lexPushLookaheadc(next[0]);
+ lexAppendc('=');
+ } else {
+ lexAppendc(c);
+ }
+ break;
+ } /* '=' */
+ case '\n': {
+ lexPushLookaheadc('\n');
+ goto EndString;
+ }
+ case (char)EOF:
+ break;
+ default:
+ lexAppendc(cur);
+ break;
+ } /* switch */
+ } while (cur != (char)EOF);
+
+EndString:
+ lexAppendc(0);
+ return lexStr();
+ } /* LexQuotedPrintable */
+
+static int yylex() {
+
+ int lexmode = LEXMODE();
+ if (lexmode == L_VALUES) {
+ int c = lexGetc();
+ if (c == ';') {
+ DBG_(("db: SEMICOLON\n"));
+ lexPushLookaheadc(c);
+ handleMoreRFC822LineBreak(c);
+ lexSkipLookahead();
+ return SEMICOLON;
+ }
+ else if (strchr("\n",c)) {
+ ++mime_lineNum;
+ /* consume all line separator(s) adjacent to each other */
+ c = lexLookahead();
+ while (strchr("\n",c)) {
+ lexSkipLookahead();
+ c = lexLookahead();
+ ++mime_lineNum;
+ }
+ DBG_(("db: LINESEP\n"));
+ return LINESEP;
+ }
+ else {
+ char *p = 0;
+ lexPushLookaheadc(c);
+ if (lexWithinMode(L_BASE64)) {
+ /* get each char and convert to bin on the fly... */
+ p = lexGetDataFromBase64();
+ yylval.str = p;
+ return STRING;
+ }
+ else if (lexWithinMode(L_QUOTED_PRINTABLE)) {
+ p = lexGetQuotedPrintable();
+ }
+ else {
+#ifdef _SUPPORT_LINE_FOLDING
+ p = lexGet1Value();
+#else
+ p = lexGetStrUntil(";\n");
+#endif
+ }
+ if (p) {
+ DBG_(("db: STRING: '%s'\n", p));
+ yylval.str = p;
+ return STRING;
+ }
+ else return 0;
+ }
+ }
+ else {
+ /* normal mode */
+ while (1) {
+ int c = lexGetc();
+ switch(c) {
+ case ':': {
+ /* consume all line separator(s) adjacent to each other */
+ /* ignoring linesep immediately after colon. */
+ c = lexLookahead();
+ while (strchr("\n",c)) {
+ lexSkipLookahead();
+ c = lexLookahead();
+ ++mime_lineNum;
+ }
+ DBG_(("db: COLON\n"));
+ return COLON;
+ }
+ case ';':
+ DBG_(("db: SEMICOLON\n"));
+ return SEMICOLON;
+ case '=':
+ DBG_(("db: EQ\n"));
+ return EQ;
+ /* ignore whitespace in this mode */
+ case '\t':
+ case ' ': continue;
+ case '\n': {
+ ++mime_lineNum;
+ continue;
+ }
+ case EOF: return 0;
+ break;
+ default: {
+ lexPushLookaheadc(c);
+ if (isalnum(c)) {
+ char *t = lexGetWord();
+ yylval.str = t;
+ if (!qstricmp(t, "begin")) {
+ return match_begin_end_name(0);
+ }
+ else if (!qstricmp(t,"end")) {
+ return match_begin_end_name(1);
+ }
+ else {
+ DBG_(("db: ID '%s'\n", t));
+ return ID;
+ }
+ }
+ else {
+ /* unknow token */
+ return 0;
+ }
+ break;
+ }
+ }
+ }
+ }
+ return 0;
+ }
+
+
+/***************************************************************************/
+/*** Public Functions ****/
+/***************************************************************************/
+
+static VObject* Parse_MIMEHelper()
+ {
+ ObjStackTop = -1;
+ mime_numErrors = 0;
+ mime_lineNum = 1;
+ vObjList = 0;
+ curObj = 0;
+
+ if (yyparse() != 0)
+ return 0;
+
+ finiLex();
+ return vObjList;
+ }
+
+/*--------------------------------------------*/
+DLLEXPORT(VObject*) Parse_MIME(const char *input, unsigned long len)
+ {
+ initLex(input, len, 0);
+ return Parse_MIMEHelper();
+ }
+
+
+#if INCLUDEMFC
+
+DLLEXPORT(VObject*) Parse_MIME_FromFile(CFile *file)
+ {
+ unsigned long startPos;
+ VObject *result;
+
+ initLex(0,-1,file);
+ startPos = file->GetPosition();
+ if (!(result = Parse_MIMEHelper()))
+ file->Seek(startPos, CFile::begin);
+ return result;
+ }
+
+#else
+
+VObject* Parse_MIME_FromFile(FILE *file)
+ {
+ VObject *result;
+ long startPos;
+
+ initLex(0,(unsigned long)-1,file);
+ startPos = ftell(file);
+ if (!(result = Parse_MIMEHelper())) {
+ fseek(file,startPos,SEEK_SET);
+ }
+ return result;
+ }
+
+DLLEXPORT(VObject*) Parse_MIME_FromFileName(char *fname)
+ {
+ FILE *fp = fopen(fname,"r");
+ if (fp) {
+ VObject* o = Parse_MIME_FromFile(fp);
+ fclose(fp);
+ return o;
+ }
+ else {
+ char msg[80];
+ sprintf(msg, "can't open file '%s' for reading\n", fname);
+ mime_error_(msg);
+ return 0;
+ }
+ }
+
+#endif
+
+/*-------------------------------------*/
+
+static MimeErrorHandler mimeErrorHandler;
+
+DLLEXPORT(void) registerMimeErrorHandler(MimeErrorHandler me)
+ {
+ mimeErrorHandler = me;
+ }
+
+void mime_error(char *s)
+ {
+ char msg[256];
+ if (mimeErrorHandler) {
+ sprintf(msg,"%s at line %d", s, mime_lineNum);
+ mimeErrorHandler(msg);
+ }
+ }
+
+void mime_error_(char *s)
+ {
+ if (mimeErrorHandler) {
+ mimeErrorHandler(s);
+ }
+ }
+
diff --git a/library/backend/vobject.cpp b/library/backend/vobject.cpp
new file mode 100644
index 0000000..af112a7
--- a/dev/null
+++ b/library/backend/vobject.cpp
@@ -0,0 +1,1210 @@
+/***************************************************************************
+(C) Copyright 1996 Apple Computer, Inc., AT&T Corp., International
+Business Machines Corporation and Siemens Rolm Communications Inc.
+
+For purposes of this license notice, the term Licensors shall mean,
+collectively, Apple Computer, Inc., AT&T Corp., International
+Business Machines Corporation and Siemens Rolm Communications Inc.
+The term Licensor shall mean any of the Licensors.
+
+Subject to acceptance of the following conditions, permission is hereby
+granted by Licensors without the need for written agreement and without
+license or royalty fees, to use, copy, modify and distribute this
+software for any purpose.
+
+The above copyright notice and the following four paragraphs must be
+reproduced in all copies of this software and any software including
+this software.
+
+THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS AND NO LICENSOR SHALL HAVE
+ANY OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS OR
+MODIFICATIONS.
+
+IN NO EVENT SHALL ANY LICENSOR BE LIABLE TO ANY PARTY FOR DIRECT,
+INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT
+OF THE USE OF THIS SOFTWARE EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGE.
+
+EACH LICENSOR SPECIFICALLY DISCLAIMS ANY WARRANTIES, EXPRESS OR IMPLIED,
+INCLUDING BUT NOT LIMITED TO ANY WARRANTY OF NONINFRINGEMENT OR THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.
+
+The software is provided with RESTRICTED RIGHTS. Use, duplication, or
+disclosure by the government are subject to restrictions set forth in
+DFARS 252.227-7013 or 48 CFR 52.227-19, as applicable.
+
+***************************************************************************/
+
+/*
+ * src: vobject.c
+ * doc: vobject and APIs to construct vobject, APIs pretty print
+ * vobject, and convert a vobject into its textual representation.
+ */
+
+#ifndef MWERKS
+#include <malloc.h>
+#endif
+
+#include "vobject_p.h"
+#include "qfiledirect_p.h"
+#include <string.h>
+#include <stdio.h>
+#include <fcntl.h>
+//#include <io.h>
+
+
+#define NAME_OF(o) o->id
+#define VALUE_TYPE(o) o->valType
+#define STRINGZ_VALUE_OF(o) o->val.strs
+#define INTEGER_VALUE_OF(o) o->val.i
+#define LONG_VALUE_OF(o) o->val.l
+#define ANY_VALUE_OF(o) o->val.any
+#define VOBJECT_VALUE_OF(o) o->val.vobj
+
+typedef union ValueItem {
+ const char *strs;
+ unsigned int i;
+ unsigned long l;
+ void *any;
+ VObject *vobj;
+ } ValueItem;
+
+struct VObject {
+ VObject *next;
+ const char *id;
+ VObject *prop;
+ unsigned short valType;
+ ValueItem val;
+ };
+
+typedef struct StrItem StrItem;
+
+struct StrItem {
+ StrItem *next;
+ const char *s;
+ unsigned int refCnt;
+ };
+
+const char** fieldedProp;
+
+
+
+/*----------------------------------------------------------------------
+ The following functions involve with memory allocation:
+ newVObject
+ deleteVObject
+ dupStr
+ deleteStr
+ newStrItem
+ deleteStrItem
+ ----------------------------------------------------------------------*/
+
+DLLEXPORT(VObject*) newVObject_(const char *id)
+{
+ VObject *p = (VObject*)malloc(sizeof(VObject));
+ p->next = 0;
+ p->id = id;
+ p->prop = 0;
+ VALUE_TYPE(p) = 0;
+ ANY_VALUE_OF(p) = 0;
+ return p;
+}
+
+DLLEXPORT(VObject*) newVObject(const char *id)
+{
+ return newVObject_(lookupStr(id));
+}
+
+DLLEXPORT(void) deleteVObject(VObject *p)
+{
+ unUseStr(p->id);
+ free(p);
+}
+
+DLLEXPORT(char*) dupStr(const char *s, unsigned int size)
+{
+ char *t;
+ if (size == 0) {
+ size = strlen(s);
+ }
+ t = (char*)malloc(size+1);
+ if (t) {
+ memcpy(t,s,size);
+ t[size] = 0;
+ return t;
+ }
+ else {
+ return (char*)0;
+ }
+}
+
+DLLEXPORT(void) deleteStr(const char *p)
+{
+ if (p) free((void*)p);
+}
+
+
+static StrItem* newStrItem(const char *s, StrItem *next)
+{
+ StrItem *p = (StrItem*)malloc(sizeof(StrItem));
+ p->next = next;
+ p->s = s;
+ p->refCnt = 1;
+ return p;
+}
+
+static void deleteStrItem(StrItem *p)
+{
+ free((void*)p);
+}
+
+
+/*----------------------------------------------------------------------
+ The following function provide accesses to VObject's value.
+ ----------------------------------------------------------------------*/
+
+DLLEXPORT(const char*) vObjectName(VObject *o)
+{
+ return NAME_OF(o);
+}
+
+DLLEXPORT(void) setVObjectName(VObject *o, const char* id)
+{
+ NAME_OF(o) = id;
+}
+
+DLLEXPORT(const char*) vObjectStringZValue(VObject *o)
+{
+ return STRINGZ_VALUE_OF(o);
+}
+
+DLLEXPORT(void) setVObjectStringZValue(VObject *o, const char *s)
+{
+ STRINGZ_VALUE_OF(o) = dupStr(s,0);
+ VALUE_TYPE(o) = VCVT_STRINGZ;
+}
+
+DLLEXPORT(void) setVObjectStringZValue_(VObject *o, const char *s)
+{
+ STRINGZ_VALUE_OF(o) = s;
+ VALUE_TYPE(o) = VCVT_STRINGZ;
+}
+
+DLLEXPORT(unsigned int) vObjectIntegerValue(VObject *o)
+{
+ return INTEGER_VALUE_OF(o);
+}
+
+DLLEXPORT(void) setVObjectIntegerValue(VObject *o, unsigned int i)
+{
+ INTEGER_VALUE_OF(o) = i;
+ VALUE_TYPE(o) = VCVT_UINT;
+}
+
+DLLEXPORT(unsigned long) vObjectLongValue(VObject *o)
+{
+ return LONG_VALUE_OF(o);
+}
+
+DLLEXPORT(void) setVObjectLongValue(VObject *o, unsigned long l)
+{
+ LONG_VALUE_OF(o) = l;
+ VALUE_TYPE(o) = VCVT_ULONG;
+}
+
+DLLEXPORT(void*) vObjectAnyValue(VObject *o)
+{
+ return ANY_VALUE_OF(o);
+}
+
+DLLEXPORT(void) setVObjectAnyValue(VObject *o, void *t)
+{
+ ANY_VALUE_OF(o) = t;
+ VALUE_TYPE(o) = VCVT_RAW;
+}
+
+DLLEXPORT(VObject*) vObjectVObjectValue(VObject *o)
+{
+ return VOBJECT_VALUE_OF(o);
+}
+
+DLLEXPORT(void) setVObjectVObjectValue(VObject *o, VObject *p)
+{
+ VOBJECT_VALUE_OF(o) = p;
+ VALUE_TYPE(o) = VCVT_VOBJECT;
+}
+
+DLLEXPORT(int) vObjectValueType(VObject *o)
+{
+ return VALUE_TYPE(o);
+}
+
+
+/*----------------------------------------------------------------------
+ The following functions can be used to build VObject.
+ ----------------------------------------------------------------------*/
+
+DLLEXPORT(VObject*) addVObjectProp(VObject *o, VObject *p)
+{
+ /* circular link list pointed to tail */
+ /*
+ o {next,id,prop,val}
+ V
+ pn {next,id,prop,val}
+ V
+ ...
+ p1 {next,id,prop,val}
+ V
+ pn
+ -->
+ o {next,id,prop,val}
+ V
+ pn {next,id,prop,val}
+ V
+ p {next,id,prop,val}
+ ...
+ p1 {next,id,prop,val}
+ V
+ pn
+ */
+
+ VObject *tail = o->prop;
+ if (tail) {
+ p->next = tail->next;
+ o->prop = tail->next = p;
+ }
+ else {
+ o->prop = p->next = p;
+ }
+ return p;
+}
+
+DLLEXPORT(VObject*) addProp(VObject *o, const char *id)
+{
+ return addVObjectProp(o,newVObject(id));
+}
+
+DLLEXPORT(VObject*) addProp_(VObject *o, const char *id)
+{
+ return addVObjectProp(o,newVObject_(id));
+}
+
+DLLEXPORT(void) addList(VObject **o, VObject *p)
+{
+ p->next = 0;
+ if (*o == 0) {
+ *o = p;
+ }
+ else {
+ VObject *t = *o;
+ while (t->next) {
+ t = t->next;
+ }
+ t->next = p;
+ }
+}
+
+DLLEXPORT(VObject*) nextVObjectInList(VObject *o)
+{
+ return o->next;
+}
+
+DLLEXPORT(VObject*) setValueWithSize_(VObject *prop, void *val, unsigned int size)
+{
+ VObject *sizeProp;
+ setVObjectAnyValue(prop, val);
+ sizeProp = addProp(prop,VCDataSizeProp);
+ setVObjectLongValue(sizeProp, size);
+ return prop;
+}
+
+DLLEXPORT(VObject*) setValueWithSize(VObject *prop, void *val, unsigned int size)
+{
+ void *p = dupStr((const char *)val,size);
+ return setValueWithSize_(prop,p,p?size:0);
+}
+
+DLLEXPORT(void) initPropIterator(VObjectIterator *i, VObject *o)
+{
+ i->start = o->prop;
+ i->next = 0;
+}
+
+DLLEXPORT(void) initVObjectIterator(VObjectIterator *i, VObject *o)
+{
+ i->start = o->next;
+ i->next = 0;
+}
+
+DLLEXPORT(int) moreIteration(VObjectIterator *i)
+{
+ return (i->start && (i->next==0 || i->next!=i->start));
+}
+
+DLLEXPORT(VObject*) nextVObject(VObjectIterator *i)
+{
+ if (i->start && i->next != i->start) {
+ if (i->next == 0) {
+ i->next = i->start->next;
+ return i->next;
+ }
+ else {
+ i->next = i->next->next;
+ return i->next;
+ }
+ }
+ else return (VObject*)0;
+}
+
+DLLEXPORT(VObject*) isAPropertyOf(VObject *o, const char *id)
+{
+ VObjectIterator i;
+ initPropIterator(&i,o);
+ while (moreIteration(&i)) {
+ VObject *each = nextVObject(&i);
+ if (!qstricmp(id,each->id))
+ return each;
+ }
+ return (VObject*)0;
+}
+
+DLLEXPORT(VObject*) addGroup(VObject *o, const char *g)
+{
+ /*
+ a.b.c
+ -->
+ prop(c)
+ prop(VCGrouping=b)
+ prop(VCGrouping=a)
+ */
+ char *dot = strrchr(g,'.');
+ if (dot) {
+ VObject *p, *t;
+ char *gs, *n = dot+1;
+ gs = dupStr(g,0); /* so we can write to it. */
+ /* used to be
+ * t = p = addProp_(o,lookupProp_(n));
+ */
+ t = p = addProp_(o,lookupProp(n));
+ dot = strrchr(gs,'.');
+ *dot = 0;
+ do {
+ dot = strrchr(gs,'.');
+ if (dot) {
+ n = dot+1;
+ *dot=0;
+ }
+ else
+ n = gs;
+ /* property(VCGroupingProp=n);
+ * and the value may have VCGrouping property
+ */
+ t = addProp(t,VCGroupingProp);
+ setVObjectStringZValue(t,lookupProp_(n));
+ } while (n != gs);
+ deleteStr(gs);
+ return p;
+ }
+ else
+ return addProp_(o,lookupProp(g));
+}
+
+DLLEXPORT(VObject*) addPropValue(VObject *o, const char *p, const char *v)
+{
+ VObject *prop;
+ prop = addProp(o,p);
+ setVObjectStringZValue_(prop, strdup( v ) );
+ return prop;
+}
+
+DLLEXPORT(VObject*) addPropSizedValue_(VObject *o, const char *p, const char *v,
+ unsigned int size)
+{
+ VObject *prop;
+ prop = addProp(o,p);
+ setValueWithSize_(prop, (void*)v, size);
+ return prop;
+}
+
+DLLEXPORT(VObject*) addPropSizedValue(VObject *o, const char *p, const char *v,
+ unsigned int size)
+{
+ return addPropSizedValue_(o,p,dupStr(v,size),size);
+}
+
+
+DLLEXPORT(void) cleanVObject(VObject *o)
+{
+ if (o == 0) return;
+ if (o->prop) {
+ /* destroy time: cannot use the iterator here.
+ Have to break the cycle in the circular link
+ list and turns it into regular NULL-terminated
+ list -- since at some point of destruction,
+ the reference entry for the iterator to work
+ will not longer be valid.
+ */
+ VObject *p;
+ p = o->prop->next;
+ o->prop->next = 0;
+ do {
+ VObject *t = p->next;
+ cleanVObject(p);
+ p = t;
+ } while (p);
+ }
+ switch (VALUE_TYPE(o)) {
+ case VCVT_STRINGZ:
+ case VCVT_RAW:
+ // assume they are all allocated by malloc.
+ free((char*)STRINGZ_VALUE_OF(o));
+ break;
+ case VCVT_VOBJECT:
+ cleanVObject(VOBJECT_VALUE_OF(o));
+ break;
+ }
+ deleteVObject(o);
+}
+
+DLLEXPORT(void) cleanVObjects(VObject *list)
+{
+ while (list) {
+ VObject *t = list;
+ list = nextVObjectInList(list);
+ cleanVObject(t);
+ }
+}
+
+/*----------------------------------------------------------------------
+ The following is a String Table Facilities.
+ ----------------------------------------------------------------------*/
+
+#define STRTBLSIZE 255
+
+static StrItem *strTbl[STRTBLSIZE];
+
+static unsigned int hashStr(const char *s)
+{
+ unsigned int h = 0;
+ int i;
+ for (i=0;s[i];i++) {
+ h += s[i]*i;
+ }
+ return h % STRTBLSIZE;
+}
+
+DLLEXPORT(const char*) lookupStr(const char *s)
+{
+ StrItem *t;
+ unsigned int h = hashStr(s);
+ if ((t = strTbl[h]) != 0) {
+ do {
+ if (qstricmp(t->s,s) == 0) {
+ t->refCnt++;
+ return t->s;
+ }
+ t = t->next;
+ } while (t);
+ }
+ s = dupStr(s,0);
+ strTbl[h] = newStrItem(s,strTbl[h]);
+ return s;
+}
+
+DLLEXPORT(void) unUseStr(const char *s)
+{
+ StrItem *t, *p;
+ unsigned int h = hashStr(s);
+ if ((t = strTbl[h]) != 0) {
+ p = t;
+ do {
+ if (qstricmp(t->s,s) == 0) {
+ t->refCnt--;
+ if (t->refCnt == 0) {
+ if (p == strTbl[h]) {
+ strTbl[h] = t->next;
+ }
+ else {
+ p->next = t->next;
+ }
+ deleteStr(t->s);
+ deleteStrItem(t);
+ return;
+ }
+ }
+ p = t;
+ t = t->next;
+ } while (t);
+ }
+}
+
+DLLEXPORT(void) cleanStrTbl()
+{
+ int i;
+ for (i=0; i<STRTBLSIZE;i++) {
+ StrItem *t = strTbl[i];
+ while (t) {
+ StrItem *p;
+ deleteStr(t->s);
+ p = t;
+ t = t->next;
+ deleteStrItem(p);
+ } while (t);
+ strTbl[i] = 0;
+ }
+}
+
+
+struct PreDefProp {
+ const char *name;
+ const char *alias;
+ const char** fields;
+ unsigned int flags;
+ };
+
+/* flags in PreDefProp */
+#define PD_BEGIN 0x1
+#define PD_INTERNAL 0x2
+
+static const char *adrFields[] = {
+ VCPostalBoxProp,
+ VCExtAddressProp,
+ VCStreetAddressProp,
+ VCCityProp,
+ VCRegionProp,
+ VCPostalCodeProp,
+ VCCountryNameProp,
+ 0
+};
+
+static const char *nameFields[] = {
+ VCFamilyNameProp,
+ VCGivenNameProp,
+ VCAdditionalNamesProp,
+ VCNamePrefixesProp,
+ VCNameSuffixesProp,
+ NULL
+ };
+
+static const char *orgFields[] = {
+ VCOrgNameProp,
+ VCOrgUnitProp,
+ VCOrgUnit2Prop,
+ VCOrgUnit3Prop,
+ VCOrgUnit4Prop,
+ NULL
+ };
+
+static const char *AAlarmFields[] = {
+ VCRunTimeProp,
+ VCSnoozeTimeProp,
+ VCRepeatCountProp,
+ VCAudioContentProp,
+ 0
+ };
+
+/* ExDate -- has unamed fields */
+/* RDate -- has unamed fields */
+
+static const char *DAlarmFields[] = {
+ VCRunTimeProp,
+ VCSnoozeTimeProp,
+ VCRepeatCountProp,
+ VCDisplayStringProp,
+ 0
+ };
+
+static const char *MAlarmFields[] = {
+ VCRunTimeProp,
+ VCSnoozeTimeProp,
+ VCRepeatCountProp,
+ VCEmailAddressProp,
+ VCNoteProp,
+ 0
+ };
+
+static const char *PAlarmFields[] = {
+ VCRunTimeProp,
+ VCSnoozeTimeProp,
+ VCRepeatCountProp,
+ VCProcedureNameProp,
+ 0
+ };
+
+static struct PreDefProp propNames[] = {
+ { VC7bitProp, 0, 0, 0 },
+ { VC8bitProp, 0, 0, 0 },
+ { VCAAlarmProp, 0, AAlarmFields, 0 },
+ { VCAdditionalNamesProp, 0, 0, 0 },
+ { VCAdrProp, 0, adrFields, 0 },
+ { VCAgentProp, 0, 0, 0 },
+ { VCAIFFProp, 0, 0, 0 },
+ { VCAOLProp, 0, 0, 0 },
+ { VCAppleLinkProp, 0, 0, 0 },
+ { VCAttachProp, 0, 0, 0 },
+ { VCAttendeeProp, 0, 0, 0 },
+ { VCATTMailProp, 0, 0, 0 },
+ { VCAudioContentProp, 0, 0, 0 },
+ { VCAVIProp, 0, 0, 0 },
+ { VCBase64Prop, 0, 0, 0 },
+ { VCBBSProp, 0, 0, 0 },
+ { VCBirthDateProp, 0, 0, 0 },
+ { VCBMPProp, 0, 0, 0 },
+ { VCBodyProp, 0, 0, 0 },
+ { VCBusinessRoleProp, 0, 0, 0 },
+ { VCCalProp, 0, 0, PD_BEGIN },
+ { VCCaptionProp, 0, 0, 0 },
+ { VCCardProp, 0, 0, PD_BEGIN },
+ { VCCarProp, 0, 0, 0 },
+ { VCCategoriesProp, 0, 0, 0 },
+ { VCCellularProp, 0, 0, 0 },
+ { VCCGMProp, 0, 0, 0 },
+ { VCCharSetProp, 0, 0, 0 },
+ { VCCIDProp, VCContentIDProp, 0, 0 },
+ { VCCISProp, 0, 0, 0 },
+ { VCCityProp, 0, 0, 0 },
+ { VCClassProp, 0, 0, 0 },
+ { VCCommentProp, 0, 0, 0 },
+ { VCCompletedProp, 0, 0, 0 },
+ { VCContentIDProp, 0, 0, 0 },
+ { VCCountryNameProp, 0, 0, 0 },
+ { VCDAlarmProp, 0, DAlarmFields, 0 },
+ { VCDataSizeProp, 0, 0, PD_INTERNAL },
+ { VCDayLightProp, 0, 0, 0 },
+ { VCDCreatedProp, 0, 0, 0 },
+ { VCDeliveryLabelProp, 0, 0, 0 },
+ { VCDescriptionProp, 0, 0, 0 },
+ { VCDIBProp, 0, 0, 0 },
+ { VCDisplayStringProp, 0, 0, 0 },
+ { VCDomesticProp, 0, 0, 0 },
+ { VCDTendProp, 0, 0, 0 },
+ { VCDTstartProp, 0, 0, 0 },
+ { VCDueProp, 0, 0, 0 },
+ { VCEmailAddressProp, 0, 0, 0 },
+ { VCEncodingProp, 0, 0, 0 },
+ { VCEndProp, 0, 0, 0 },
+ { VCEventProp, 0, 0, PD_BEGIN },
+ { VCEWorldProp, 0, 0, 0 },
+ { VCExNumProp, 0, 0, 0 },
+ { VCExpDateProp, 0, 0, 0 },
+ { VCExpectProp, 0, 0, 0 },
+ { VCExtAddressProp, 0, 0, 0 },
+ { VCFamilyNameProp, 0, 0, 0 },
+ { VCFaxProp, 0, 0, 0 },
+ { VCFullNameProp, 0, 0, 0 },
+ { VCGeoLocationProp, 0, 0, 0 },
+ { VCGeoProp, 0, 0, 0 },
+ { VCGIFProp, 0, 0, 0 },
+ { VCGivenNameProp, 0, 0, 0 },
+ { VCGroupingProp, 0, 0, 0 },
+ { VCHomeProp, 0, 0, 0 },
+ { VCIBMMailProp, 0, 0, 0 },
+ { VCInlineProp, 0, 0, 0 },
+ { VCInternationalProp, 0, 0, 0 },
+ { VCInternetProp, 0, 0, 0 },
+ { VCISDNProp, 0, 0, 0 },
+ { VCJPEGProp, 0, 0, 0 },
+ { VCLanguageProp, 0, 0, 0 },
+ { VCLastModifiedProp, 0, 0, 0 },
+ { VCLastRevisedProp, 0, 0, 0 },
+ { VCLocationProp, 0, 0, 0 },
+ { VCLogoProp, 0, 0, 0 },
+ { VCMailerProp, 0, 0, 0 },
+ { VCMAlarmProp, 0, MAlarmFields, 0 },
+ { VCMCIMailProp, 0, 0, 0 },
+ { VCMessageProp, 0, 0, 0 },
+ { VCMETProp, 0, 0, 0 },
+ { VCModemProp, 0, 0, 0 },
+ { VCMPEG2Prop, 0, 0, 0 },
+ { VCMPEGProp, 0, 0, 0 },
+ { VCMSNProp, 0, 0, 0 },
+ { VCNamePrefixesProp, 0, 0, 0 },
+ { VCNameProp, 0, nameFields, 0 },
+ { VCNameSuffixesProp, 0, 0, 0 },
+ { VCNoteProp, 0, 0, 0 },
+ { VCOrgNameProp, 0, 0, 0 },
+ { VCOrgProp, 0, orgFields, 0 },
+ { VCOrgUnit2Prop, 0, 0, 0 },
+ { VCOrgUnit3Prop, 0, 0, 0 },
+ { VCOrgUnit4Prop, 0, 0, 0 },
+ { VCOrgUnitProp, 0, 0, 0 },
+ { VCPagerProp, 0, 0, 0 },
+ { VCPAlarmProp, 0, PAlarmFields, 0 },
+ { VCParcelProp, 0, 0, 0 },
+ { VCPartProp, 0, 0, 0 },
+ { VCPCMProp, 0, 0, 0 },
+ { VCPDFProp, 0, 0, 0 },
+ { VCPGPProp, 0, 0, 0 },
+ { VCPhotoProp, 0, 0, 0 },
+ { VCPICTProp, 0, 0, 0 },
+ { VCPMBProp, 0, 0, 0 },
+ { VCPostalBoxProp, 0, 0, 0 },
+ { VCPostalCodeProp, 0, 0, 0 },
+ { VCPostalProp, 0, 0, 0 },
+ { VCPowerShareProp, 0, 0, 0 },
+ { VCPreferredProp, 0, 0, 0 },
+ { VCPriorityProp, 0, 0, 0 },
+ { VCProcedureNameProp, 0, 0, 0 },
+ { VCProdIdProp, 0, 0, 0 },
+ { VCProdigyProp, 0, 0, 0 },
+ { VCPronunciationProp, 0, 0, 0 },
+ { VCPSProp, 0, 0, 0 },
+ { VCPublicKeyProp, 0, 0, 0 },
+ { VCQPProp, VCQuotedPrintableProp, 0, 0 },
+ { VCQuickTimeProp, 0, 0, 0 },
+ { VCQuotedPrintableProp, 0, 0, 0 },
+ { VCRDateProp, 0, 0, 0 },
+ { VCRegionProp, 0, 0, 0 },
+ { VCRelatedToProp, 0, 0, 0 },
+ { VCRepeatCountProp, 0, 0, 0 },
+ { VCResourcesProp, 0, 0, 0 },
+ { VCRNumProp, 0, 0, 0 },
+ { VCRoleProp, 0, 0, 0 },
+ { VCRRuleProp, 0, 0, 0 },
+ { VCRSVPProp, 0, 0, 0 },
+ { VCRunTimeProp, 0, 0, 0 },
+ { VCSequenceProp, 0, 0, 0 },
+ { VCSnoozeTimeProp, 0, 0, 0 },
+ { VCStartProp, 0, 0, 0 },
+ { VCStatusProp, 0, 0, 0 },
+ { VCStreetAddressProp, 0, 0, 0 },
+ { VCSubTypeProp, 0, 0, 0 },
+ { VCSummaryProp, 0, 0, 0 },
+ { VCTelephoneProp, 0, 0, 0 },
+ { VCTIFFProp, 0, 0, 0 },
+ { VCTimeZoneProp, 0, 0, 0 },
+ { VCTitleProp, 0, 0, 0 },
+ { VCTLXProp, 0, 0, 0 },
+ { VCTodoProp, 0, 0, PD_BEGIN },
+ { VCTranspProp, 0, 0, 0 },
+ { VCUniqueStringProp, 0, 0, 0 },
+ { VCURLProp, 0, 0, 0 },
+ { VCURLValueProp, 0, 0, 0 },
+ { VCValueProp, 0, 0, 0 },
+ { VCVersionProp, 0, 0, 0 },
+ { VCVideoProp, 0, 0, 0 },
+ { VCVoiceProp, 0, 0, 0 },
+ { VCWAVEProp, 0, 0, 0 },
+ { VCWMFProp, 0, 0, 0 },
+ { VCWorkProp, 0, 0, 0 },
+ { VCX400Prop, 0, 0, 0 },
+ { VCX509Prop, 0, 0, 0 },
+ { VCXRuleProp, 0, 0, 0 },
+ { 0,0,0,0 }
+ };
+
+
+static struct PreDefProp* lookupPropInfo(const char* str)
+{
+ /* brute force for now, could use a hash table here. */
+ int i;
+
+ for (i = 0; propNames[i].name; i++)
+ if (qstricmp(str, propNames[i].name) == 0) {
+ return &propNames[i];
+ }
+
+ return 0;
+}
+
+
+DLLEXPORT(const char*) lookupProp_(const char* str)
+{
+ int i;
+
+ for (i = 0; propNames[i].name; i++)
+ if (qstricmp(str, propNames[i].name) == 0) {
+ const char* s;
+ s = propNames[i].alias?propNames[i].alias:propNames[i].name;
+ return lookupStr(s);
+ }
+ return lookupStr(str);
+}
+
+
+DLLEXPORT(const char*) lookupProp(const char* str)
+{
+ int i;
+
+ for (i = 0; propNames[i].name; i++)
+ if (qstricmp(str, propNames[i].name) == 0) {
+ const char *s;
+ fieldedProp = propNames[i].fields;
+ s = propNames[i].alias?propNames[i].alias:propNames[i].name;
+ return lookupStr(s);
+ }
+ fieldedProp = 0;
+ return lookupStr(str);
+}
+
+
+/*----------------------------------------------------------------------
+ APIs to Output text form.
+ ----------------------------------------------------------------------*/
+#define OFILE_REALLOC_SIZE 256
+typedef struct OFile {
+ FILE *fp;
+ char *s;
+ int len;
+ int limit;
+ int alloc:1;
+ int fail:1;
+ } OFile;
+
+#if 0
+static void appendsOFile(OFile *fp, const char *s)
+{
+ int slen;
+ if (fp->fail) return;
+ slen = strlen(s);
+ if (fp->fp) {
+ fwrite(s,1,slen,fp->fp);
+ }
+ else {
+stuff:
+ if (fp->len + slen < fp->limit) {
+ memcpy(fp->s+fp->len,s,slen);
+ fp->len += slen;
+ return;
+ }
+ else if (fp->alloc) {
+ fp->limit = fp->limit + OFILE_REALLOC_SIZE;
+ if (OFILE_REALLOC_SIZE <= slen) fp->limit += slen;
+ fp->s = (char *) realloc(fp->s,fp->limit);
+ if (fp->s) goto stuff;
+ }
+ if (fp->alloc)
+ free(fp->s);
+ fp->s = 0;
+ fp->fail = 1;
+ }
+}
+
+static void appendcOFile(OFile *fp, char c)
+{
+ if (fp->fail) return;
+ if (fp->fp) {
+ fputc(c,fp->fp);
+ }
+ else {
+stuff:
+ if (fp->len+1 < fp->limit) {
+ fp->s[fp->len] = c;
+ fp->len++;
+ return;
+ }
+ else if (fp->alloc) {
+ fp->limit = fp->limit + OFILE_REALLOC_SIZE;
+ fp->s = (char *) realloc(fp->s,fp->limit);
+ if (fp->s) goto stuff;
+ }
+ if (fp->alloc)
+ free(fp->s);
+ fp->s = 0;
+ fp->fail = 1;
+ }
+}
+#else
+static void appendcOFile_(OFile *fp, char c)
+{
+ if (fp->fail) return;
+ if (fp->fp) {
+ fputc(c,fp->fp);
+ }
+ else {
+stuff:
+ if (fp->len+1 < fp->limit) {
+ fp->s[fp->len] = c;
+ fp->len++;
+ return;
+ }
+ else if (fp->alloc) {
+ fp->limit = fp->limit + OFILE_REALLOC_SIZE;
+ fp->s = (char *)realloc(fp->s,fp->limit);
+ if (fp->s) goto stuff;
+ }
+ if (fp->alloc)
+ free(fp->s);
+ fp->s = 0;
+ fp->fail = 1;
+ }
+}
+
+static void appendcOFile(OFile *fp, char c)
+{
+ if (c == '\n') {
+ /* write out as <CR><LF> */
+ appendcOFile_(fp,0xd);
+ appendcOFile_(fp,0xa);
+ }
+ else
+ appendcOFile_(fp,c);
+}
+
+static void appendsOFile(OFile *fp, const char *s)
+{
+ int i, slen;
+ slen = strlen(s);
+ for (i=0; i<slen; i++) {
+ appendcOFile(fp,s[i]);
+ }
+}
+
+#endif
+
+static void initOFile(OFile *fp, FILE *ofp)
+{
+ fp->fp = ofp;
+ fp->s = 0;
+ fp->len = 0;
+ fp->limit = 0;
+ fp->alloc = 0;
+ fp->fail = 0;
+}
+
+static int writeBase64(OFile *fp, unsigned char *s, long len)
+{
+ long cur = 0;
+ int i, numQuads = 0;
+ unsigned long trip;
+ unsigned char b;
+ char quad[5];
+#define MAXQUADS 16
+
+ quad[4] = 0;
+
+ while (cur < len) {
+ // collect the triplet of bytes into 'trip'
+ trip = 0;
+ for (i = 0; i < 3; i++) {
+ b = (cur < len) ? *(s + cur) : 0;
+ cur++;
+ trip = trip << 8 | b;
+ }
+ // fill in 'quad' with the appropriate four characters
+ for (i = 3; i >= 0; i--) {
+ b = (unsigned char)(trip & 0x3F);
+ trip = trip >> 6;
+ if ((3 - i) < (cur - len))
+ quad[i] = '='; // pad char
+ else if (b < 26) quad[i] = (char)b + 'A';
+ else if (b < 52) quad[i] = (char)(b - 26) + 'a';
+ else if (b < 62) quad[i] = (char)(b - 52) + '0';
+ else if (b == 62) quad[i] = '+';
+ else quad[i] = '/';
+ }
+ // now output 'quad' with appropriate whitespace and line ending
+ appendsOFile(fp, (numQuads == 0 ? " " : ""));
+ appendsOFile(fp, quad);
+ appendsOFile(fp, ((cur >= len)?"\n" :(numQuads==MAXQUADS-1?"\n" : "")));
+ numQuads = (numQuads + 1) % MAXQUADS;
+ }
+ appendcOFile(fp,'\n');
+
+ return 1;
+}
+
+static void writeQPString(OFile *fp, const char *s)
+{
+ const char *p = s;
+ while (*p) {
+ if (*p == '\n') {
+ if (p[1]) appendsOFile(fp,"=0A=");
+ }
+ appendcOFile(fp,*p);
+ p++;
+ }
+}
+
+
+
+static void writeVObject_(OFile *fp, VObject *o);
+
+static void writeValue(OFile *fp, VObject *o, unsigned long size)
+{
+ if (o == 0) return;
+ switch (VALUE_TYPE(o)) {
+ case VCVT_STRINGZ: {
+ writeQPString(fp, STRINGZ_VALUE_OF(o));
+ break;
+ }
+ case VCVT_UINT: {
+ char buf[16];
+ sprintf(buf,"%u", INTEGER_VALUE_OF(o));
+ appendsOFile(fp,buf);
+ break;
+ }
+ case VCVT_ULONG: {
+ char buf[16];
+ sprintf(buf,"%lu", LONG_VALUE_OF(o));
+ appendsOFile(fp,buf);
+ break;
+ }
+ case VCVT_RAW: {
+ appendcOFile(fp,'\n');
+ writeBase64(fp,(unsigned char*)(ANY_VALUE_OF(o)),size);
+ break;
+ }
+ case VCVT_VOBJECT:
+ appendcOFile(fp,'\n');
+ writeVObject_(fp,VOBJECT_VALUE_OF(o));
+ break;
+ }
+}
+
+static void writeAttrValue(OFile *fp, VObject *o)
+{
+ if (NAME_OF(o)) {
+ struct PreDefProp *pi;
+ pi = lookupPropInfo(NAME_OF(o));
+ if (pi && ((pi->flags & PD_INTERNAL) != 0)) return;
+ appendcOFile(fp,';');
+ appendsOFile(fp,NAME_OF(o));
+ }
+ else
+ appendcOFile(fp,';');
+ if (VALUE_TYPE(o)) {
+ appendcOFile(fp,'=');
+ writeValue(fp,o,0);
+ }
+}
+
+static void writeGroup(OFile *fp, VObject *o)
+{
+ char buf1[256];
+ char buf2[256];
+ strcpy(buf1,NAME_OF(o));
+ while ((o=isAPropertyOf(o,VCGroupingProp)) != 0) {
+ strcpy(buf2,STRINGZ_VALUE_OF(o));
+ strcat(buf2,".");
+ strcat(buf2,buf1);
+ strcpy(buf1,buf2);
+ }
+ appendsOFile(fp,buf1);
+}
+
+static int inList(const char **list, const char *s)
+{
+ if (list == 0) return 0;
+ while (*list) {
+ if (qstricmp(*list,s) == 0) return 1;
+ list++;
+ }
+ return 0;
+}
+
+static void writeProp(OFile *fp, VObject *o)
+{
+ if (NAME_OF(o)) {
+ struct PreDefProp *pi;
+ VObjectIterator t;
+ const char **fields_ = 0;
+ pi = lookupPropInfo(NAME_OF(o));
+ if (pi && ((pi->flags & PD_BEGIN) != 0)) {
+ writeVObject_(fp,o);
+ return;
+ }
+ if (isAPropertyOf(o,VCGroupingProp))
+ writeGroup(fp,o);
+ else
+ appendsOFile(fp,NAME_OF(o));
+ if (pi) fields_ = pi->fields;
+ initPropIterator(&t,o);
+ while (moreIteration(&t)) {
+ const char *s;
+ VObject *eachProp = nextVObject(&t);
+ s = NAME_OF(eachProp);
+ if (qstricmp(VCGroupingProp,s) && !inList(fields_,s))
+ writeAttrValue(fp,eachProp);
+ }
+ if (fields_) {
+ int i = 0, n = 0;
+ const char** fields = fields_;
+ /* output prop as fields */
+ appendcOFile(fp,':');
+ while (*fields) {
+ VObject *t = isAPropertyOf(o,*fields);
+ i++;
+ if (t) n = i;
+ fields++;
+ }
+ fields = fields_;
+ for (i=0;i<n;i++) {
+ writeValue(fp,isAPropertyOf(o,*fields),0);
+ fields++;
+ if (i<(n-1)) appendcOFile(fp,';');
+ }
+ }
+ }
+
+ if (VALUE_TYPE(o)) {
+ unsigned long size = 0;
+ VObject *p = isAPropertyOf(o,VCDataSizeProp);
+ if (p) size = LONG_VALUE_OF(p);
+ appendcOFile(fp,':');
+ writeValue(fp,o,size);
+ }
+
+ appendcOFile(fp,'\n');
+}
+
+static void writeVObject_(OFile *fp, VObject *o)
+{
+ if (NAME_OF(o)) {
+ struct PreDefProp *pi;
+ pi = lookupPropInfo(NAME_OF(o));
+
+ if (pi && ((pi->flags & PD_BEGIN) != 0)) {
+ VObjectIterator t;
+ const char *begin = NAME_OF(o);
+ appendsOFile(fp,"BEGIN:");
+ appendsOFile(fp,begin);
+ appendcOFile(fp,'\n');
+ initPropIterator(&t,o);
+ while (moreIteration(&t)) {
+ VObject *eachProp = nextVObject(&t);
+ writeProp(fp, eachProp);
+ }
+ appendsOFile(fp,"END:");
+ appendsOFile(fp,begin);
+ appendsOFile(fp,"\n\n");
+ }
+ }
+}
+
+void writeVObject(FILE *fp, VObject *o)
+{
+ OFile ofp;
+ // #####
+ //_setmode(_fileno(fp), _O_BINARY);
+ initOFile(&ofp,fp);
+ writeVObject_(&ofp,o);
+}
+
+DLLEXPORT(void) writeVObjectToFile(char *fname, VObject *o)
+{
+ QFileDirect f( fname);
+ if ( !f.open( IO_WriteOnly ) ) {
+ qWarning("Unable to open vobject write %s", fname);
+ return;
+ }
+
+ writeVObject( f.directHandle(),o );
+}
+
+DLLEXPORT(void) writeVObjectsToFile(char *fname, VObject *list)
+{
+ QFileDirect f( fname);
+ if ( !f.open( IO_WriteOnly ) ) {
+ qWarning("Unable to open vobject write %s", fname);
+ return;
+ }
+
+ while (list) {
+ writeVObject(f.directHandle(),list);
+ list = nextVObjectInList(list);
+ }
+}
+
+// end of source file vobject.c
diff --git a/library/backend/vobject_p.h b/library/backend/vobject_p.h
new file mode 100644
index 0000000..b6a2c0a
--- a/dev/null
+++ b/library/backend/vobject_p.h
@@ -0,0 +1,401 @@
+/***************************************************************************
+(C) Copyright 1996 Apple Computer, Inc., AT&T Corp., International
+Business Machines Corporation and Siemens Rolm Communications Inc.
+
+For purposes of this license notice, the term Licensors shall mean,
+collectively, Apple Computer, Inc., AT&T Corp., International
+Business Machines Corporation and Siemens Rolm Communications Inc.
+The term Licensor shall mean any of the Licensors.
+
+Subject to acceptance of the following conditions, permission is hereby
+granted by Licensors without the need for written agreement and without
+license or royalty fees, to use, copy, modify and distribute this
+software for any purpose.
+
+The above copyright notice and the following four paragraphs must be
+reproduced in all copies of this software and any software including
+this software.
+
+THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS AND NO LICENSOR SHALL HAVE
+ANY OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS OR
+MODIFICATIONS.
+
+IN NO EVENT SHALL ANY LICENSOR BE LIABLE TO ANY PARTY FOR DIRECT,
+INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT
+OF THE USE OF THIS SOFTWARE EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGE.
+
+EACH LICENSOR SPECIFICALLY DISCLAIMS ANY WARRANTIES, EXPRESS OR IMPLIED,
+INCLUDING BUT NOT LIMITED TO ANY WARRANTY OF NONINFRINGEMENT OR THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.
+
+The software is provided with RESTRICTED RIGHTS. Use, duplication, or
+disclosure by the government are subject to restrictions set forth in
+DFARS 252.227-7013 or 48 CFR 52.227-19, as applicable.
+
+***************************************************************************/
+
+/*
+
+The vCard/vCalendar C interface is implemented in the set
+of files as follows:
+
+vcc.y, yacc source, and vcc.c, the yacc output you will use
+implements the core parser
+
+vobject.c implements an API that insulates the caller from
+the parser and changes in the vCard/vCalendar BNF
+
+port.h defines compilation environment dependent stuff
+
+vcc.h and vobject.h are header files for their .c counterparts
+
+vcaltmp.h and vcaltmp.c implement vCalendar "macro" functions
+which you may find useful.
+
+test.c is a standalone test driver that exercises some of
+the features of the APIs provided. Invoke test.exe on a
+VCARD/VCALENDAR input text file and you will see the pretty
+print output of the internal representation (this pretty print
+output should give you a good idea of how the internal
+representation looks like -- there is one such output in the
+following too). Also, a file with the .out suffix is generated
+to show that the internal representation can be written back
+in the original text format.
+
+For more information on this API see the readme.txt file
+which accompanied this distribution.
+
+ Also visit:
+
+ http://www.versit.com
+ http://www.ralden.com
+
+*/
+
+
+#ifndef __VOBJECT_H__
+#define __VOBJECT_H__ 1
+
+#include <qstring.h>
+
+#define vCardClipboardFormat "+//ISBN 1-887687-00-9::versit::PDI//vCard"
+#define vCalendarClipboardFormat "+//ISBN 1-887687-00-9::versit::PDI//vCalendar"
+
+/* The above strings vCardClipboardFormat and vCalendarClipboardFormat
+are globally unique IDs which can be used to generate clipboard format
+ID's as per the requirements of a specific platform. For example, in
+Windows they are used as the parameter in a call to RegisterClipboardFormat.
+For example:
+
+ CLIPFORMAT foo = RegisterClipboardFormat(vCardClipboardFormat);
+
+*/
+
+#define vCardMimeType "text/x-vCard"
+#define vCalendarMimeType "text/x-vCalendar"
+
+#undef DLLEXPORT
+#include <qglobal.h>
+#if defined(Q_WS_WIN)
+#define DLLEXPORT(t) __declspec(dllexport) t
+#else
+#define DLLEXPORT(t) t
+#endif
+
+#ifndef FALSE
+#define FALSE 0
+#endif
+#ifndef TRUE
+#define TRUE 1
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+
+
+#define VC7bitProp "7BIT"
+#define VC8bitProp "8BIT"
+#define VCAAlarmProp "AALARM"
+#define VCAdditionalNamesProp "ADDN"
+#define VCAdrProp "ADR"
+#define VCAgentProp "AGENT"
+#define VCAIFFProp "AIFF"
+#define VCAOLProp "AOL"
+#define VCAppleLinkProp "APPLELINK"
+#define VCAttachProp "ATTACH"
+#define VCAttendeeProp "ATTENDEE"
+#define VCATTMailProp "ATTMAIL"
+#define VCAudioContentProp "AUDIOCONTENT"
+#define VCAVIProp "AVI"
+#define VCBase64Prop "BASE64"
+#define VCBBSProp "BBS"
+#define VCBirthDateProp "BDAY"
+#define VCBMPProp "BMP"
+#define VCBodyProp "BODY"
+#define VCBusinessRoleProp "ROLE"
+#define VCCalProp "VCALENDAR"
+#define VCCaptionProp "CAP"
+#define VCCardProp "VCARD"
+#define VCCarProp "CAR"
+#define VCCategoriesProp "CATEGORIES"
+#define VCCellularProp "CELL"
+#define VCCGMProp "CGM"
+#define VCCharSetProp "CS"
+#define VCCIDProp "CID"
+#define VCCISProp "CIS"
+#define VCCityProp "L"
+#define VCClassProp "CLASS"
+#define VCCommentProp "NOTE"
+#define VCCompletedProp "COMPLETED"
+#define VCContentIDProp "CONTENT-ID"
+#define VCCountryNameProp "C"
+#define VCDAlarmProp "DALARM"
+#define VCDataSizeProp "DATASIZE"
+#define VCDayLightProp "DAYLIGHT"
+#define VCDCreatedProp "DCREATED"
+#define VCDeliveryLabelProp "LABEL"
+#define VCDescriptionProp "DESCRIPTION"
+#define VCDIBProp "DIB"
+#define VCDisplayStringProp "DISPLAYSTRING"
+#define VCDomesticProp "DOM"
+#define VCDTendProp "DTEND"
+#define VCDTstartProp "DTSTART"
+#define VCDueProp "DUE"
+#define VCEmailAddressProp "EMAIL"
+#define VCEncodingProp "ENCODING"
+#define VCEndProp "END"
+#define VCEventProp "VEVENT"
+#define VCEWorldProp "EWORLD"
+#define VCExNumProp "EXNUM"
+#define VCExpDateProp "EXDATE"
+#define VCExpectProp "EXPECT"
+#define VCExtAddressProp "EXT ADD"
+#define VCFamilyNameProp "F"
+#define VCFaxProp "FAX"
+#define VCFullNameProp "FN"
+#define VCGeoProp "GEO"
+#define VCGeoLocationProp "GEO"
+#define VCGIFProp "GIF"
+#define VCGivenNameProp "G"
+#define VCGroupingProp "Grouping"
+#define VCHomeProp "HOME"
+#define VCIBMMailProp "IBMMail"
+#define VCInlineProp "INLINE"
+#define VCInternationalProp "INTL"
+#define VCInternetProp "INTERNET"
+#define VCISDNProp "ISDN"
+#define VCJPEGProp "JPEG"
+#define VCLanguageProp "LANG"
+#define VCLastModifiedProp "LAST-MODIFIED"
+#define VCLastRevisedProp "REV"
+#define VCLocationProp "LOCATION"
+#define VCLogoProp "LOGO"
+#define VCMailerProp "MAILER"
+#define VCMAlarmProp "MALARM"
+#define VCMCIMailProp "MCIMAIL"
+#define VCMessageProp "MSG"
+#define VCMETProp "MET"
+#define VCModemProp "MODEM"
+#define VCMPEG2Prop "MPEG2"
+#define VCMPEGProp "MPEG"
+#define VCMSNProp "MSN"
+#define VCNamePrefixesProp "NPRE"
+#define VCNameProp "N"
+#define VCNameSuffixesProp "NSUF"
+#define VCNoteProp "NOTE"
+#define VCOrgNameProp "ORGNAME"
+#define VCOrgProp "ORG"
+#define VCOrgUnit2Prop "OUN2"
+#define VCOrgUnit3Prop "OUN3"
+#define VCOrgUnit4Prop "OUN4"
+#define VCOrgUnitProp "OUN"
+#define VCPagerProp "PAGER"
+#define VCPAlarmProp "PALARM"
+#define VCParcelProp "PARCEL"
+#define VCPartProp "PART"
+#define VCPCMProp "PCM"
+#define VCPDFProp "PDF"
+#define VCPGPProp "PGP"
+#define VCPhotoProp "PHOTO"
+#define VCPICTProp "PICT"
+#define VCPMBProp "PMB"
+#define VCPostalBoxProp "BOX"
+#define VCPostalCodeProp "PC"
+#define VCPostalProp "POSTAL"
+#define VCPowerShareProp "POWERSHARE"
+#define VCPreferredProp "PREF"
+#define VCPriorityProp "PRIORITY"
+#define VCProcedureNameProp "PROCEDURENAME"
+#define VCProdIdProp "PRODID"
+#define VCProdigyProp "PRODIGY"
+#define VCPronunciationProp "SOUND"
+#define VCPSProp "PS"
+#define VCPublicKeyProp "KEY"
+#define VCQPProp "QP"
+#define VCQuickTimeProp "QTIME"
+#define VCQuotedPrintableProp "QUOTED-PRINTABLE"
+#define VCRDateProp "RDATE"
+#define VCRegionProp "R"
+#define VCRelatedToProp "RELATED-TO"
+#define VCRepeatCountProp "REPEATCOUNT"
+#define VCResourcesProp "RESOURCES"
+#define VCRNumProp "RNUM"
+#define VCRoleProp "ROLE"
+#define VCRRuleProp "RRULE"
+#define VCRSVPProp "RSVP"
+#define VCRunTimeProp "RUNTIME"
+#define VCSequenceProp "SEQUENCE"
+#define VCSnoozeTimeProp "SNOOZETIME"
+#define VCStartProp "START"
+#define VCStatusProp "STATUS"
+#define VCStreetAddressProp "STREET"
+#define VCSubTypeProp "SUBTYPE"
+#define VCSummaryProp "SUMMARY"
+#define VCTelephoneProp "TEL"
+#define VCTIFFProp "TIFF"
+#define VCTimeZoneProp "TZ"
+#define VCTitleProp "TITLE"
+#define VCTLXProp "TLX"
+#define VCTodoProp "VTODO"
+#define VCTranspProp "TRANSP"
+#define VCUniqueStringProp "UID"
+#define VCURLProp "URL"
+#define VCURLValueProp "URLVAL"
+#define VCValueProp "VALUE"
+#define VCVersionProp "VERSION"
+#define VCVideoProp "VIDEO"
+#define VCVoiceProp "VOICE"
+#define VCWAVEProp "WAVE"
+#define VCWMFProp "WMF"
+#define VCWorkProp "WORK"
+#define VCX400Prop "X400"
+#define VCX509Prop "X509"
+#define VCXRuleProp "XRULE"
+
+
+typedef struct VObject VObject;
+
+typedef struct VObjectIterator {
+ VObject* start;
+ VObject* next;
+ } VObjectIterator;
+
+extern DLLEXPORT(VObject*) newVObject(const char *id);
+extern DLLEXPORT(void) deleteVObject(VObject *p);
+extern DLLEXPORT(char*) dupStr(const char *s, unsigned int size);
+extern DLLEXPORT(void) deleteStr(const char *p);
+extern DLLEXPORT(void) unUseStr(const char *s);
+
+extern DLLEXPORT(void) setVObjectName(VObject *o, const char* id);
+extern DLLEXPORT(void) setVObjectStringZValue(VObject *o, const char *s);
+extern DLLEXPORT(void) setVObjectStringZValue_(VObject *o, const char *s);
+extern DLLEXPORT(void) setVObjectIntegerValue(VObject *o, unsigned int i);
+extern DLLEXPORT(void) setVObjectLongValue(VObject *o, unsigned long l);
+extern DLLEXPORT(void) setVObjectAnyValue(VObject *o, void *t);
+extern DLLEXPORT(VObject*) setValueWithSize(VObject *prop, void *val, unsigned int size);
+extern DLLEXPORT(VObject*) setValueWithSize_(VObject *prop, void *val, unsigned int size);
+
+extern DLLEXPORT(const char*) vObjectName(VObject *o);
+extern DLLEXPORT(const char*) vObjectStringZValue(VObject *o);
+extern DLLEXPORT(unsigned int) vObjectIntegerValue(VObject *o);
+extern DLLEXPORT(unsigned long) vObjectLongValue(VObject *o);
+extern DLLEXPORT(void*) vObjectAnyValue(VObject *o);
+extern DLLEXPORT(VObject*) vObjectVObjectValue(VObject *o);
+extern DLLEXPORT(void) setVObjectVObjectValue(VObject *o, VObject *p);
+
+extern DLLEXPORT(VObject*) addVObjectProp(VObject *o, VObject *p);
+extern DLLEXPORT(VObject*) addProp(VObject *o, const char *id);
+extern DLLEXPORT(VObject*) addProp_(VObject *o, const char *id);
+extern DLLEXPORT(VObject*) addPropValue(VObject *o, const char *p, const char *v);
+extern DLLEXPORT(VObject*) addPropSizedValue_(VObject *o, const char *p, const char *v, unsigned int size);
+extern DLLEXPORT(VObject*) addPropSizedValue(VObject *o, const char *p, const char *v, unsigned int size);
+extern DLLEXPORT(VObject*) addGroup(VObject *o, const char *g);
+extern DLLEXPORT(void) addList(VObject **o, VObject *p);
+
+extern DLLEXPORT(VObject*) isAPropertyOf(VObject *o, const char *id);
+
+extern DLLEXPORT(VObject*) nextVObjectInList(VObject *o);
+extern DLLEXPORT(void) initPropIterator(VObjectIterator *i, VObject *o);
+extern DLLEXPORT(int) moreIteration(VObjectIterator *i);
+extern DLLEXPORT(VObject*) nextVObject(VObjectIterator *i);
+
+extern DLLEXPORT(const char*) lookupStr(const char *s);
+extern DLLEXPORT(void) cleanStrTbl();
+
+extern DLLEXPORT(void) cleanVObject(VObject *o);
+extern DLLEXPORT(void) cleanVObjects(VObject *list);
+
+extern DLLEXPORT(const char*) lookupProp(const char* str);
+extern DLLEXPORT(const char*) lookupProp_(const char* str);
+
+extern DLLEXPORT(void) writeVObjectToFile(char *fname, VObject *o);
+extern DLLEXPORT(void) writeVObjectsToFile(char *fname, VObject *list);
+
+extern DLLEXPORT(int) vObjectValueType(VObject *o);
+
+/* return type of vObjectValueType: */
+#define VCVT_NOVALUE 0
+ /* if the VObject has no value associated with it. */
+#define VCVT_STRINGZ 1
+ /* if the VObject has value set by setVObjectStringZValue. */
+#define VCVT_UINT 2
+ /* if the VObject has value set by setVObjectIntegerValue. */
+#define VCVT_ULONG 3
+ /* if the VObject has value set by setVObjectLongValue. */
+#define VCVT_RAW 4
+ /* if the VObject has value set by setVObjectAnyValue. */
+#define VCVT_VOBJECT 5
+ /* if the VObject has value set by setVObjectVObjectValue. */
+
+extern const char** fieldedProp;
+
+/***************************************************
+ * The methods below are implemented in vcc.c (generated from vcc.y )
+ ***************************************************/
+
+/* NOTE regarding printVObject and writeVObject
+
+The functions below are not exported from the DLL because they
+take a FILE* as a parameter, which cannot be passed across a DLL
+interface (at least that is my experience). Instead you can use
+their companion functions which take file names or pointers
+to memory. However, if you are linking this code into
+your build directly then you may find them a more convenient API
+and you can go ahead and use them. If you try to use them with
+the DLL LIB you will get a link error.
+*/
+extern void writeVObject(FILE *fp, VObject *o);
+
+
+
+typedef void (*MimeErrorHandler)(char *);
+
+extern DLLEXPORT(void) registerMimeErrorHandler(MimeErrorHandler);
+
+extern DLLEXPORT(VObject*) Parse_MIME(const char *input, unsigned long len);
+extern DLLEXPORT(VObject*) Parse_MIME_FromFileName(char* fname);
+
+
+/* NOTE regarding Parse_MIME_FromFile
+The function above, Parse_MIME_FromFile, comes in two flavors,
+neither of which is exported from the DLL. Each version takes
+a CFile or FILE* as a parameter, neither of which can be
+passed across a DLL interface (at least that is my experience).
+If you are linking this code into your build directly then
+you may find them a more convenient API that the other flavors
+that take a file name. If you use them with the DLL LIB you
+will get a link error.
+*/
+
+
+#if INCLUDEMFC
+extern VObject* Parse_MIME_FromFile(CFile *file);
+#else
+extern VObject* Parse_MIME_FromFile(FILE *file);
+#endif
+
+#endif /* __VOBJECT_H__ */
+
+