diff options
author | toma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2009-11-25 17:56:58 +0000 |
---|---|---|
committer | toma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2009-11-25 17:56:58 +0000 |
commit | bcb704366cb5e333a626c18c308c7e0448a8e69f (patch) | |
tree | f0d6ab7d78ecdd9207cf46536376b44b91a1ca71 /kopete/protocols/groupwise/libgroupwise | |
download | tdenetwork-bcb704366cb5e333a626c18c308c7e0448a8e69f.tar.gz tdenetwork-bcb704366cb5e333a626c18c308c7e0448a8e69f.zip |
Copy the KDE 3.5 branch to branches/trinity for new KDE 3.5 features.
BUG:215923
git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/kdenetwork@1054174 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'kopete/protocols/groupwise/libgroupwise')
148 files changed, 20023 insertions, 0 deletions
diff --git a/kopete/protocols/groupwise/libgroupwise/Makefile.am b/kopete/protocols/groupwise/libgroupwise/Makefile.am new file mode 100644 index 00000000..2df23a01 --- /dev/null +++ b/kopete/protocols/groupwise/libgroupwise/Makefile.am @@ -0,0 +1,40 @@ +INCLUDES = -I$(top_srcdir)/kopete/protocols/groupwise/libgroupwise/qca/src \ + -I$(top_srcdir)/kopete/protocols/groupwise \ + $(all_includes) +METASOURCES = AUTO + +AM_CFLAGS = -DUSE_TLSHANDLER + +CLEANFILES = securestream.moc + +SUBDIRS = qca tasks + +KDE_OPTIONS = nofinal + +noinst_HEADERS = connector.h tlshandler.h qcatlshandler.h bytestream.h \ + gwclientstream.h securestream.h stream.h coreprotocol.h gwfield.h gwerror.h \ + usertransfer.h eventtransfer.h transfer.h request.h requestfactory.h client.h task.h \ + safedelete.h response.h rtf2html.h userdetailsmanager.h eventprotocol.h \ + inputprotocolbase.h responseprotocol.h privacymanager.h gwchatrooms.h chatroommanager.h + +noinst_LTLIBRARIES = libgroupwise.la libgwtest.la +libgroupwise_la_COMPILE_FIRST = securestream.moc +libgroupwise_la_SOURCES = bytestream.cpp chatroommanager.cpp client.cpp \ + connector.cpp coreprotocol.cpp eventprotocol.cpp eventtransfer.cpp gwclientstream.cpp \ + gwerror.cpp gwfield.cpp gwglobal.cpp inputprotocolbase.cpp privacymanager.cpp \ + qcatlshandler.cpp request.cpp requestfactory.cpp response.cpp responseprotocol.cpp rtf.cc \ + safedelete.cpp securestream.cpp stream.cpp task.cpp tlshandler.cpp transfer.cpp \ + transferbase.cpp userdetailsmanager.cpp usertransfer.cpp +libgroupwise_la_LDFLAGS = -no-undefined $(all_libraries) +libgroupwise_la_LIBADD = tasks/libgroupwise_tasks.la -lqt-mt qca/src/libqca.la + +tests_COMPILE_FIRST = libgroupwise.la libgwtest.la + +libgwtest_la_SOURCES = coreprotocol.cpp eventtransfer.cpp \ + gwfield.cpp request.cpp requestfactory.cpp transfer.cpp usertransfer.cpp \ + client.cpp task.cpp safedelete.cpp gwclientstream.cpp qcatlshandler.cpp \ + stream.cpp tlshandler.cpp response.cpp connector.cpp securestream.cpp \ + bytestream.cpp +libgwtest_la_LDFLAGS = $(all_libraries) -no-undefined +libgwtest_la_LIBADD = qca/src/libqca.la \ + $(top_builddir)/kopete/protocols/groupwise/libgroupwise/tasks/libgroupwise_tasks.la -lqt-mt diff --git a/kopete/protocols/groupwise/libgroupwise/bytestream.cpp b/kopete/protocols/groupwise/libgroupwise/bytestream.cpp new file mode 100644 index 00000000..328c4a20 --- /dev/null +++ b/kopete/protocols/groupwise/libgroupwise/bytestream.cpp @@ -0,0 +1,269 @@ +/* + * bytestream.cpp - base class for bytestreams + * Copyright (C) 2003 Justin Karneges + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include"bytestream.h" + +// CS_NAMESPACE_BEGIN + +//! \class ByteStream bytestream.h +//! \brief Base class for "bytestreams" +//! +//! This class provides a basic framework for a "bytestream", here defined +//! as a bi-directional, asynchronous pipe of data. It can be used to create +//! several different kinds of bytestream-applications, such as a console or +//! TCP connection, or something more abstract like a security layer or tunnel, +//! all with the same interface. The provided functions make creating such +//! classes simpler. ByteStream is a pure-virtual class, so you do not use it +//! on its own, but instead through a subclass such as \a BSocket. +//! +//! The signals connectionClosed(), delayedCloseFinished(), readyRead(), +//! bytesWritten(), and error() serve the exact same function as those from +//! <A HREF="http://doc.trolltech.com/3.1/qsocket.html">QSocket</A>. +//! +//! The simplest way to create a ByteStream is to reimplement isOpen(), close(), +//! and tryWrite(). Call appendRead() whenever you want to make data available for +//! reading. ByteStream will take care of the buffers with regards to the caller, +//! and will call tryWrite() when the write buffer gains data. It will be your +//! job to call tryWrite() whenever it is acceptable to write more data to +//! the underlying system. +//! +//! If you need more advanced control, reimplement read(), write(), bytesAvailable(), +//! and/or bytesToWrite() as necessary. +//! +//! Use appendRead(), appendWrite(), takeRead(), and takeWrite() to modify the +//! buffers. If you have more advanced requirements, the buffers can be accessed +//! directly with readBuf() and writeBuf(). +//! +//! Also available are the static convenience functions ByteStream::appendArray() +//! and ByteStream::takeArray(), which make dealing with byte queues very easy. + +class ByteStream::Private +{ +public: + Private() {} + + QByteArray readBuf, writeBuf; +}; + +//! +//! Constructs a ByteStream object with parent \a parent. +ByteStream::ByteStream(QObject *parent) +:QObject(parent) +{ + d = new Private; +} + +//! +//! Destroys the object and frees allocated resources. +ByteStream::~ByteStream() +{ + delete d; +} + +//! +//! Returns TRUE if the stream is open, meaning that you can write to it. +bool ByteStream::isOpen() const +{ + return false; +} + +//! +//! Closes the stream. If there is data in the write buffer then it will be +//! written before actually closing the stream. Once all data has been written, +//! the delayedCloseFinished() signal will be emitted. +//! \sa delayedCloseFinished() +void ByteStream::close() +{ +} + +//! +//! Writes array \a a to the stream. +void ByteStream::write(const QByteArray &a) +{ + if(!isOpen()) + return; + + bool doWrite = bytesToWrite() == 0 ? true: false; + appendWrite(a); + if(doWrite) + tryWrite(); +} + +//! +//! Reads bytes \a bytes of data from the stream and returns them as an array. If \a bytes is 0, then +//! \a read will return all available data. +QByteArray ByteStream::read(int bytes) +{ + return takeRead(bytes); +} + +//! +//! Returns the number of bytes available for reading. +int ByteStream::bytesAvailable() const +{ + return d->readBuf.size(); +} + +//! +//! Returns the number of bytes that are waiting to be written. +int ByteStream::bytesToWrite() const +{ + return d->writeBuf.size(); +} + +//! +//! Writes string \a cs to the stream. +void ByteStream::write(const QCString &cs) +{ + QByteArray block(cs.length()); + memcpy(block.data(), cs.data(), block.size()); + write(block); +} + +//! +//! Clears the read buffer. +void ByteStream::clearReadBuffer() +{ + d->readBuf.resize(0); +} + +//! +//! Clears the write buffer. +void ByteStream::clearWriteBuffer() +{ + d->writeBuf.resize(0); +} + +//! +//! Appends \a block to the end of the read buffer. +void ByteStream::appendRead(const QByteArray &block) +{ + appendArray(&d->readBuf, block); +} + +//! +//! Appends \a block to the end of the write buffer. +void ByteStream::appendWrite(const QByteArray &block) +{ + appendArray(&d->writeBuf, block); +} + +//! +//! Returns \a size bytes from the start of the read buffer. +//! If \a size is 0, then all available data will be returned. +//! If \a del is TRUE, then the bytes are also removed. +QByteArray ByteStream::takeRead(int size, bool del) +{ + return takeArray(&d->readBuf, size, del); +} + +//! +//! Returns \a size bytes from the start of the write buffer. +//! If \a size is 0, then all available data will be returned. +//! If \a del is TRUE, then the bytes are also removed. +QByteArray ByteStream::takeWrite(int size, bool del) +{ + return takeArray(&d->writeBuf, size, del); +} + +//! +//! Returns a reference to the read buffer. +QByteArray & ByteStream::readBuf() +{ + return d->readBuf; +} + +//! +//! Returns a reference to the write buffer. +QByteArray & ByteStream::writeBuf() +{ + return d->writeBuf; +} + +//! +//! Attempts to try and write some bytes from the write buffer, and returns the number +//! successfully written or -1 on error. The default implementation returns -1. +int ByteStream::tryWrite() +{ + return -1; +} + +//! +//! Append array \a b to the end of the array pointed to by \a a. +void ByteStream::appendArray(QByteArray *a, const QByteArray &b) +{ + int oldsize = a->size(); + a->resize(oldsize + b.size()); + memcpy(a->data() + oldsize, b.data(), b.size()); +} + +//! +//! Returns \a size bytes from the start of the array pointed to by \a from. +//! If \a size is 0, then all available data will be returned. +//! If \a del is TRUE, then the bytes are also removed. +QByteArray ByteStream::takeArray(QByteArray *from, int size, bool del) +{ + QByteArray a; + if(size == 0) { + a = from->copy(); + if(del) + from->resize(0); + } + else { + if(size > (int)from->size()) + size = from->size(); + a.resize(size); + char *r = from->data(); + memcpy(a.data(), r, size); + if(del) { + int newsize = from->size()-size; + memmove(r, r+size, newsize); + from->resize(newsize); + } + } + return a; +} + void connectionClosed(); + void delayedCloseFinished(); + void readyRead(); + void bytesWritten(int); + void error(int); + +//! \fn void ByteStream::connectionClosed() +//! This signal is emitted when the remote end of the stream closes. + +//! \fn void ByteStream::delayedCloseFinished() +//! This signal is emitted when all pending data has been written to the stream +//! after an attempt to close. + +//! \fn void ByteStream::readyRead() +//! This signal is emitted when data is available to be read. + +//! \fn void ByteStream::bytesWritten(int x); +//! This signal is emitted when data has been successfully written to the stream. +//! \a x is the number of bytes written. + +//! \fn void ByteStream::error(int code) +//! This signal is emitted when an error occurs in the stream. The reason for +//! error is indicated by \a code. + +// CS_NAMESPACE_END + +#include "bytestream.moc" diff --git a/kopete/protocols/groupwise/libgroupwise/bytestream.h b/kopete/protocols/groupwise/libgroupwise/bytestream.h new file mode 100644 index 00000000..c33b3976 --- /dev/null +++ b/kopete/protocols/groupwise/libgroupwise/bytestream.h @@ -0,0 +1,78 @@ +/* + * bytestream.h - base class for bytestreams + * Copyright (C) 2003 Justin Karneges + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef CS_BYTESTREAM_H +#define CS_BYTESTREAM_H + +#include<qobject.h> +#include<qcstring.h> + +// CS_NAMESPACE_BEGIN + +// CS_EXPORT_BEGIN +class ByteStream : public QObject +{ + Q_OBJECT +public: + enum Error { ErrRead, ErrWrite, ErrCustom = 10 }; + ByteStream(QObject *parent=0); + virtual ~ByteStream()=0; + + virtual bool isOpen() const; + virtual void close(); + virtual void write(const QByteArray &); + virtual QByteArray read(int bytes=0); + virtual int bytesAvailable() const; + virtual int bytesToWrite() const; + + void write(const QCString &); + + static void appendArray(QByteArray *a, const QByteArray &b); + static QByteArray takeArray(QByteArray *from, int size=0, bool del=true); + +signals: + void connectionClosed(); + void delayedCloseFinished(); + void readyRead(); + void bytesWritten(int); + void error(int); + +protected: + void clearReadBuffer(); + void clearWriteBuffer(); + void appendRead(const QByteArray &); + void appendWrite(const QByteArray &); + QByteArray takeRead(int size=0, bool del=true); + QByteArray takeWrite(int size=0, bool del=true); + QByteArray & readBuf(); + QByteArray & writeBuf(); + virtual int tryWrite(); + +private: +//! \if _hide_doc_ + class Private; + Private *d; +//! \endif +}; +// CS_EXPORT_END + +// CS_NAMESPACE_END + +#endif diff --git a/kopete/protocols/groupwise/libgroupwise/chatroommanager.cpp b/kopete/protocols/groupwise/libgroupwise/chatroommanager.cpp new file mode 100644 index 00000000..adbb66de --- /dev/null +++ b/kopete/protocols/groupwise/libgroupwise/chatroommanager.cpp @@ -0,0 +1,140 @@ +/* + Kopete Groupwise Protocol + chatroommanager.cpp - tracks our knowledge of server side chatrooms + + Copyright (c) 2005 SUSE Linux Products GmbH http://www.suse.com + + Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#include <qmap.h> +#include <qvaluelist.h> + +#include <kdebug.h> + +#include "client.h" +#include "tasks/chatcountstask.h" +#include "tasks/chatpropertiestask.h" +#include "tasks/searchchattask.h" + +#include "chatroommanager.h" + +ChatroomManager::ChatroomManager( Client * parent, const char *name) + : QObject(parent, name), m_client( parent ), m_replace( false ) +{ +} + +ChatroomManager::~ChatroomManager() +{ +} + +void ChatroomManager::updateRooms() +{ + getChatrooms( !m_rooms.isEmpty() ); +} + +GroupWise::ChatroomMap ChatroomManager::rooms() +{ + return m_rooms; +} + +void ChatroomManager::getChatrooms( bool refresh ) +{ + m_replace = !refresh; + SearchChatTask * sct = new SearchChatTask( m_client->rootTask() ); + sct->search( ( refresh ? SearchChatTask::SinceLastSearch : SearchChatTask::FetchAll ) ); + connect( sct, SIGNAL( finished() ), SLOT( slotGotChatroomList() ) ); + sct->go( true ); +} + +void ChatroomManager::slotGotChatroomList() +{ + kdDebug ( GROUPWISE_DEBUG_GLOBAL ) << k_funcinfo << endl; + SearchChatTask * sct = (SearchChatTask *)sender(); + if ( sct ) + { + if ( m_replace ) + m_rooms.clear(); + + QValueList<ChatroomSearchResult> roomsFound = sct->results(); + QValueList<ChatroomSearchResult>::Iterator it = roomsFound.begin(); + const QValueList<ChatroomSearchResult>::Iterator end = roomsFound.end(); + for ( ; it != end; ++it ) + { + GroupWise::Chatroom c( *it ); + m_rooms.insert( c.displayName, c ); + } + } + emit updated(); +} + +void ChatroomManager::updateCounts() +{ + ChatCountsTask * cct = new ChatCountsTask( m_client->rootTask() ); + connect( cct, SIGNAL( finished() ), SLOT( slotGotChatCounts() ) ); + cct->go( true ); +} + +void ChatroomManager::slotGotChatCounts() +{ + ChatCountsTask * cct = (ChatCountsTask *)sender(); + if ( cct ) + { + QMap< QString, int > newCounts = cct->results(); + QMap< QString, int >::iterator it = newCounts.begin(); + const QMap< QString, int >::iterator end = newCounts.end(); + + for ( ; it != end; ++it ) + if ( m_rooms.contains( it.key() ) ) + m_rooms[ it.key() ].participantsCount = it.data(); + } + emit updated(); +} + +void ChatroomManager::requestProperties( const QString & displayName ) +{ + if ( 0 /*m_rooms.contains( displayName ) */) + emit gotProperties( m_rooms[ displayName ] ); + else + { + ChatPropertiesTask * cpt = new ChatPropertiesTask( m_client->rootTask() ); + cpt->setChat( displayName ); + connect ( cpt, SIGNAL( finished() ), SLOT( slotGotChatProperties() ) ); + cpt->go( true ); + } +} + +void ChatroomManager::slotGotChatProperties() +{ + kdDebug( GROUPWISE_DEBUG_GLOBAL ) << k_funcinfo << endl; + ChatPropertiesTask * cpt = (ChatPropertiesTask *)sender(); + if ( cpt ) + { + Chatroom room = m_rooms[ cpt->m_chat ]; + room.displayName = cpt->m_chat; + room.ownerDN = cpt->m_ownerDn; + room.description = cpt->m_description; + room.disclaimer = cpt->m_disclaimer; + room.query = cpt->m_query; + room.archive = ( cpt->m_archive == "0" ); + room.maxUsers = cpt->m_maxUsers.toInt(); + room.topic = cpt->m_topic; + room.creatorDN = cpt->m_creatorDn; + room.createdOn = cpt->m_creationTime; + room.acl = cpt->m_aclEntries; + room.chatRights = cpt->m_rights; + m_rooms.insert( room.displayName, room ); + emit gotProperties( room ); + } +} + +#include "chatroommanager.moc" diff --git a/kopete/protocols/groupwise/libgroupwise/chatroommanager.h b/kopete/protocols/groupwise/libgroupwise/chatroommanager.h new file mode 100644 index 00000000..4d0e888b --- /dev/null +++ b/kopete/protocols/groupwise/libgroupwise/chatroommanager.h @@ -0,0 +1,66 @@ +/* + Kopete Groupwise Protocol + chatroommanager.h - tracks our knowledge of server side chatrooms + + Copyright (c) 2005 SUSE Linux Products GmbH http://www.suse.com + + Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#ifndef CHATROOMMANAGER_H +#define CHATROOMMANAGER_H + +#include <qobject.h> + +#include "gwchatrooms.h" + +class Client; + +/** + * Keeps a record of the server side chatrooms + * @author SUSE Linux Products GmbH + */ +class ChatroomManager : public QObject +{ + Q_OBJECT + public: + ChatroomManager( Client * client, const char *name = 0); + ~ChatroomManager(); + GroupWise::ChatroomMap rooms(); + void requestProperties( const QString & displayName ); + void updateRooms(); + void updateCounts(); + signals: + void gotProperties( const GroupWise::Chatroom & ); + void updated(); + protected: + void getChatrooms( bool refresh ); + protected slots: + /** + * Used to initialise the list of chatrooms in response to a SearchChatTask. + */ + void slotGotChatroomList(); + /** + * Used to update the user counts of chatrooms. + */ + void slotGotChatCounts(); + /** + * Get the properties of a specific room. + */ + void slotGotChatProperties(); + private: + Client * m_client; + GroupWise::ChatroomMap m_rooms; + bool m_replace; +}; + +#endif diff --git a/kopete/protocols/groupwise/libgroupwise/client.cpp b/kopete/protocols/groupwise/libgroupwise/client.cpp new file mode 100644 index 00000000..274a9ea8 --- /dev/null +++ b/kopete/protocols/groupwise/libgroupwise/client.cpp @@ -0,0 +1,541 @@ +/* + Kopete Groupwise Protocol + client.cpp - The main interface for the Groupwise protocol + + Copyright (c) 2004 SUSE Linux AG http://www.suse.com + (c) 2008 Novell, Inc. + + Based on Iris, Copyright (C) 2003 Justin Karneges + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#include <qapplication.h> +#include <qtimer.h> + +#include "chatroommanager.h" +#include "gwclientstream.h" +#include "privacymanager.h" +#include "requestfactory.h" +#include "task.h" +#include "tasks/conferencetask.h" +#include "tasks/connectiontask.h" +#include "tasks/createconferencetask.h" +#include "tasks/getdetailstask.h" +#include "tasks/getstatustask.h" +#include "tasks/joinconferencetask.h" +#include "tasks/keepalivetask.h" +#include "tasks/leaveconferencetask.h" +#include "tasks/logintask.h" +#include "tasks/rejectinvitetask.h" +#include "tasks/sendinvitetask.h" +#include "tasks/sendmessagetask.h" +#include "tasks/setstatustask.h" +#include "tasks/statustask.h" +#include "tasks/typingtask.h" +#include "userdetailsmanager.h" +#include "client.h" + +class Client::ClientPrivate +{ +public: + ClientPrivate() {} + + ClientStream *stream; + int id_seed; + Task *root; + QString host, user, userDN, pass; + QString osname, tzname, clientName, clientVersion; + uint port; +/* int tzoffset;*/ + bool active; + RequestFactory * requestFactory; + ChatroomManager * chatroomMgr; + UserDetailsManager * userDetailsMgr; + PrivacyManager * privacyMgr; + uint protocolVersion; + QValueList<GroupWise::CustomStatus> customStatuses; + QTimer * keepAliveTimer; +}; + +Client::Client(QObject *par, uint protocolVersion ) +:QObject(par, "groupwiseclient") +{ + d = new ClientPrivate; +/* d->tzoffset = 0;*/ + d->active = false; + d->osname = "N/A"; + d->clientName = "N/A"; + d->clientVersion = "0.0"; + d->id_seed = 0xaaaa; + d->root = new Task(this, true); + d->chatroomMgr = 0; + d->requestFactory = new RequestFactory; + d->userDetailsMgr = new UserDetailsManager( this, "userdetailsmgr" ); + d->privacyMgr = new PrivacyManager( this, "privacymgr" ); + d->stream = 0; + d->protocolVersion = protocolVersion; + // Sends regular keepalives so the server knows we are still running + d->keepAliveTimer = new QTimer( this ); + connect( d->keepAliveTimer, SIGNAL( timeout() ), SLOT( sendKeepAlive() ) ); +} + +Client::~Client() +{ + delete d->root; + delete d->requestFactory; + delete d->userDetailsMgr; + delete d; +} + +void Client::connectToServer( ClientStream *s, const NovellDN &server, bool auth ) +{ + d->stream = s; + //connect(d->stream, SIGNAL(connected()), SLOT(streamConnected())); + //connect(d->stream, SIGNAL(handshaken()), SLOT(streamHandshaken())); + connect(d->stream, SIGNAL(error(int)), SLOT(streamError(int))); + //connect(d->stream, SIGNAL(sslCertificateReady(const QSSLCert &)), SLOT(streamSSLCertificateReady(const QSSLCert &))); + connect(d->stream, SIGNAL(readyRead()), SLOT(streamReadyRead())); + //connect(d->stream, SIGNAL(closeFinished()), SLOT(streamCloseFinished())); + + d->stream->connectToServer(server, auth); +} + +void Client::setOSName(const QString &name) +{ + d->osname = name; +} + +void Client::setClientName(const QString &s) +{ + d->clientName = s; +} + +void Client::setClientVersion(const QString &s) +{ + d->clientVersion = s; +} + +void Client::start( const QString &host, const uint port, const QString &userId, const QString &pass ) +{ + d->host = host; + d->port = port; + d->user = userId; + d->pass = pass; + + initialiseEventTasks(); + + LoginTask * login = new LoginTask( d->root ); + + connect( login, SIGNAL( gotMyself( const GroupWise::ContactDetails & ) ), + this, SIGNAL( accountDetailsReceived( const GroupWise::ContactDetails & ) ) ); + + connect( login, SIGNAL( gotFolder( const FolderItem & ) ), + this, SIGNAL( folderReceived( const FolderItem & ) ) ); + + connect( login, SIGNAL( gotContact( const ContactItem & ) ), + this, SIGNAL( contactReceived( const ContactItem & ) ) ); + + connect( login, SIGNAL( gotContactUserDetails( const GroupWise::ContactDetails & ) ), + this, SIGNAL( contactUserDetailsReceived( const GroupWise::ContactDetails & ) ) ) ; + + connect( login, SIGNAL( gotPrivacySettings( bool, bool, const QStringList &, const QStringList & ) ), + privacyManager(), SLOT( slotGotPrivacySettings( bool, bool, const QStringList &, const QStringList & ) ) ); + + connect( login, SIGNAL( gotCustomStatus( const GroupWise::CustomStatus & ) ), + SLOT( lt_gotCustomStatus( const GroupWise::CustomStatus & ) ) ); + + connect( login, SIGNAL( gotKeepalivePeriod( int ) ), SLOT( lt_gotKeepalivePeriod( int ) ) ); + + connect( login, SIGNAL( finished() ), this, SLOT( lt_loginFinished() ) ); + + login->initialise(); + login->go( true ); + + d->active = true; +} + +void Client::close() +{ + debug( "Client::close()" ); + d->keepAliveTimer->stop(); + if(d->stream) { + d->stream->disconnect(this); + d->stream->close(); + d->stream = 0; + } +} + +QString Client::host() +{ + return d->host; +} + +int Client::port() +{ + return d->port; +} + +QValueList<GroupWise::CustomStatus> Client::customStatuses() +{ + return d->customStatuses; +} + +void Client::initialiseEventTasks() +{ + // The StatusTask handles incoming status changes + StatusTask * st = new StatusTask( d->root ); // FIXME - add an additional EventRoot? + connect( st, SIGNAL( gotStatus( const QString &, Q_UINT16, const QString & ) ), SIGNAL( statusReceived( const QString &, Q_UINT16, const QString & ) ) ); + // The ConferenceTask handles incoming conference events, messages, joins, leaves, etc + ConferenceTask * ct = new ConferenceTask( d->root ); + connect( ct, SIGNAL( message( const ConferenceEvent & ) ), SLOT( ct_messageReceived( const ConferenceEvent & ) ) ); + connect( ct, SIGNAL( typing( const ConferenceEvent & ) ), SIGNAL( contactTyping( const ConferenceEvent & ) ) ); + connect( ct, SIGNAL( notTyping( const ConferenceEvent & ) ), SIGNAL( contactNotTyping( const ConferenceEvent & ) ) ); + connect( ct, SIGNAL( joined( const ConferenceEvent & ) ), SIGNAL( conferenceJoinNotifyReceived( const ConferenceEvent & ) ) ); + connect( ct, SIGNAL( left( const ConferenceEvent & ) ), SIGNAL( conferenceLeft( const ConferenceEvent & ) ) ); + connect( ct, SIGNAL( invited( const ConferenceEvent & ) ), SIGNAL( invitationReceived( const ConferenceEvent & ) ) ); + connect( ct, SIGNAL( otherInvited( const ConferenceEvent & ) ), SIGNAL( inviteNotifyReceived( const ConferenceEvent & ) ) ); + connect( ct, SIGNAL( invitationDeclined( const ConferenceEvent & ) ), SIGNAL( invitationDeclined( const ConferenceEvent & ) ) ); + connect( ct, SIGNAL( closed( const ConferenceEvent & ) ), SIGNAL( conferenceClosed( const ConferenceEvent & ) ) ); + connect( ct, SIGNAL( autoReply( const ConferenceEvent & ) ), SIGNAL( autoReplyReceived( const ConferenceEvent & ) ) ); + connect( ct, SIGNAL( broadcast( const ConferenceEvent & ) ), SIGNAL( broadcastReceived( const ConferenceEvent & ) ) ); + connect( ct, SIGNAL( systemBroadcast( const ConferenceEvent & ) ), SIGNAL( systemBroadcastReceived( const ConferenceEvent & ) ) ); + + + // The ConnectionTask handles incoming connection events + ConnectionTask* cont = new ConnectionTask( d->root ); + connect( cont, SIGNAL( connectedElsewhere() ), SIGNAL( connectedElsewhere() ) ); +} + +void Client::setStatus( GroupWise::Status status, const QString & reason, const QString & autoReply ) +{ + debug( QString("Setting status to %1").arg( status ) );; + SetStatusTask * sst = new SetStatusTask( d->root ); + sst->status( status, reason, autoReply ); + connect( sst, SIGNAL( finished() ), this, SLOT( sst_statusChanged() ) ); + sst->go( true ); + // TODO: set status change in progress flag +} + +void Client::requestStatus( const QString & userDN ) +{ + GetStatusTask * gst = new GetStatusTask( d->root ); + gst->userDN( userDN ); + connect( gst, SIGNAL( gotStatus( const QString &, Q_UINT16, const QString & ) ), SIGNAL( statusReceived( const QString &, Q_UINT16, const QString & ) ) ); + gst->go( true ); +} + +void Client::sendMessage( const QStringList & addresseeDNs, const OutgoingMessage & message ) +{ + SendMessageTask * smt = new SendMessageTask( d->root ); + smt->message( addresseeDNs, message ); + connect( smt, SIGNAL( finished() ), SLOT( smt_messageSent() ) ); + smt->go( true ); +} + +void Client::sendTyping( const GroupWise::ConferenceGuid & conferenceGuid, bool typing ) +{ + TypingTask * tt = new TypingTask( d->root ); + tt->typing( conferenceGuid, typing ); + tt->go( true ); +} + +void Client::createConference( const int clientId ) +{ + QStringList dummy; + createConference( clientId, dummy ); +} + +void Client::createConference( const int clientId, const QStringList & participants ) +{ + CreateConferenceTask * cct = new CreateConferenceTask( d->root ); + cct->conference( clientId, participants ); + connect( cct, SIGNAL( finished() ), SLOT( cct_conferenceCreated() ) ); + cct->go( true ); +} +void Client::requestDetails( const QStringList & userDNs ) +{ + GetDetailsTask * gdt = new GetDetailsTask( d->root ); + gdt->userDNs( userDNs ); + connect( gdt, SIGNAL( gotContactUserDetails( const GroupWise::ContactDetails & ) ), + this, SIGNAL( contactUserDetailsReceived( const GroupWise::ContactDetails & ) ) ); + gdt->go( true ); +} + +void Client::joinConference( const GroupWise::ConferenceGuid & guid ) +{ + JoinConferenceTask * jct = new JoinConferenceTask( d->root ); + jct->join( guid ); + connect( jct, SIGNAL( finished() ), SLOT( jct_joinConfCompleted() ) ); + jct->go( true ); +} + +void Client::rejectInvitation( const GroupWise::ConferenceGuid & guid ) +{ + RejectInviteTask * rit = new RejectInviteTask ( d->root ); + rit->reject( guid ); + // we don't do anything with the results of this task + rit->go( true ); +} + +void Client::leaveConference( const GroupWise::ConferenceGuid & guid ) +{ + LeaveConferenceTask * lct = new LeaveConferenceTask( d->root ); + lct->leave( guid ); + //connect( lct, SIGNAL( finished() ), SLOT( lct_leftConference() ) ); + lct->go( true ); +} + +void Client::sendInvitation( const GroupWise::ConferenceGuid & guid, const QString & dn, const GroupWise::OutgoingMessage & message ) +{ + SendInviteTask * sit = new SendInviteTask( d->root ); + QStringList invitees( dn ); + sit->invite( guid, dn, message ); + sit->go( true ); +} + +// SLOTS // +void Client::streamError( int error ) +{ + debug( QString( "CLIENT ERROR (Error %1)" ).arg( error ) ); +} + +void Client::streamReadyRead() +{ + debug( "CLIENT STREAM READY READ" ); + // take the incoming transfer and distribute it to the task tree + Transfer * transfer = d->stream->read(); + distribute( transfer ); +} + +void Client::lt_loginFinished() +{ + debug( "Client::lt_loginFinished()" ); + const LoginTask * lt = (LoginTask *)sender(); + if ( lt->success() ) + { + debug( "Client::lt_loginFinished() LOGIN SUCCEEDED" ); + // set our initial status + SetStatusTask * sst = new SetStatusTask( d->root ); + sst->status( GroupWise::Available, QString::null, QString::null ); + sst->go( true ); + emit loggedIn(); + // fetch details for any privacy list items that aren't in our contact list. + // There is a chicken-and-egg case regarding this: We need the privacy before reading the contact list so + // blocked contacts are shown as blocked. But we need not fetch user details for the privacy lists + // before reading the contact list, as many privacy items' details are already in the contact list + privacyManager()->getDetailsForPrivacyLists(); + } + else + { + debug( "Client::lt_loginFinished() LOGIN FAILED" ); + emit loginFailed(); + } + // otherwise client should disconnect and signal failure that way?? +} + +void Client::sst_statusChanged() +{ + const SetStatusTask * sst = (SetStatusTask *)sender(); + if ( sst->success() ) + { + emit ourStatusChanged( sst->requestedStatus(), sst->awayMessage(), sst->autoReply() ); + } +} + +void Client::ct_messageReceived( const ConferenceEvent & messageEvent ) +{ + debug( "parsing received message's RTF" ); + ConferenceEvent transformedEvent = messageEvent; + RTF2HTML parser; + QString rtf = messageEvent.message; + if ( !rtf.isEmpty() ) + transformedEvent.message = parser.Parse( rtf.latin1(), "" ); + + // fixes for RTF to HTML conversion problems + // we can drop these once the server reenables the sending of unformatted text + // redundant linebreak at the end of the message + QRegExp rx(" </span> </span> </span><br>$"); + transformedEvent.message.replace( rx, "</span></span></span>" ); + // missing linebreak after first line of an encrypted message + QRegExp ry("-----BEGIN PGP MESSAGE----- </span> </span> </span>"); + transformedEvent.message.replace( ry, "-----BEGIN PGP MESSAGE-----</span></span></span><br/>" ); + + emit messageReceived( transformedEvent ); +} + +void Client::cct_conferenceCreated() +{ + const CreateConferenceTask * cct = ( CreateConferenceTask * )sender(); + if ( cct->success() ) + { + emit conferenceCreated( cct->clientConfId(), cct->conferenceGUID() ); + } + else + { + emit conferenceCreationFailed( cct->clientConfId(), cct->statusCode() ); + } +} + +void Client::jct_joinConfCompleted() +{ + const JoinConferenceTask * jct = ( JoinConferenceTask * )sender(); +#ifdef LIBGW_DEBUG + debug( QString( "Joined conference %1, participants are: " ).arg( jct->guid() ) ); + QStringList parts = jct->participants(); + for ( QStringList::Iterator it = parts.begin(); it != parts.end(); ++it ) + debug( QString( " - %1" ).arg(*it) ); + debug( "invitees are: " ); + QStringList invitees = jct->invitees(); + for ( QStringList::Iterator it = invitees.begin(); it != invitees.end(); ++it ) + debug( QString( " - %1" ).arg(*it) ); +#endif + emit conferenceJoined( jct->guid(), jct->participants(), jct->invitees() ); +} + +void Client::lt_gotCustomStatus( const GroupWise::CustomStatus & custom ) +{ + d->customStatuses.append( custom ); +} + +// INTERNALS // + +QString Client::userId() +{ + return d->user; +} + +void Client::setUserDN( const QString & userDN ) +{ + d->userDN = userDN; +} + +QString Client::userDN() +{ + return d->userDN; +} + +QString Client::password() +{ + return d->pass; +} + +QString Client::userAgent() +{ + return QString::fromLatin1( "%1/%2 (%3)" ).arg( d->clientName, d->clientVersion, d->osname ); +} + +QCString Client::ipAddress() +{ + // TODO: remove hardcoding + return "10.10.11.103"; +} + +void Client::distribute( Transfer * transfer ) +{ + if( !rootTask()->take( transfer ) ) + debug( "CLIENT: root task refused transfer" ); + // at this point the transfer is no longer needed + delete transfer; +} + +void Client::send( Request * request ) +{ + debug( "CLIENT::send()" ); + if( !d->stream ) + { + debug( "CLIENT - NO STREAM TO SEND ON!"); + return; + } +// QString out = request.toString(); +// debug(QString("Client: outgoing: [\n%1]\n").arg(out)); +// xmlOutgoing(out); + + d->stream->write( request ); +} + +void Client::debug( const QString &str ) +{ +#ifdef LIBGW_USE_KDEBUG + kdDebug( GROUPWISE_DEBUG_LIBGW ) << "debug: " << str << endl; +#else + qDebug( "CLIENT: %s\n", str.ascii() ); +#endif +} + +QString Client::genUniqueId() +{ + QString s; + s.sprintf("a%x", d->id_seed); + d->id_seed += 0x10; + return s; +} + +PrivacyManager * Client::privacyManager() +{ + return d->privacyMgr; +} + +RequestFactory * Client::requestFactory() +{ + return d->requestFactory; +} + +UserDetailsManager * Client::userDetailsManager() +{ + return d->userDetailsMgr; +} + +Task * Client::rootTask() +{ + return d->root; +} + +uint Client::protocolVersion() const +{ + return d->protocolVersion; +} + +ChatroomManager * Client::chatroomManager() +{ + if ( !d->chatroomMgr ) + d->chatroomMgr = new ChatroomManager( this, "chatroommgr" ); + return d->chatroomMgr; +} + +void Client::lt_gotKeepalivePeriod( int period ) +{ + d->keepAliveTimer->start( period * 60 * 1000 ); +} + +void Client::sendKeepAlive() +{ + KeepAliveTask * kat = new KeepAliveTask( d->root ); + kat->setup(); + kat->go( true ); +} + +void Client::smt_messageSent() +{ + const SendMessageTask * smt = ( SendMessageTask * )sender(); + if ( smt->success() ) + { + debug( "message sent OK" ); + } + else + { + debug( "message sending failed!" ); + emit messageSendingFailed(); + } +} + +#include "client.moc" diff --git a/kopete/protocols/groupwise/libgroupwise/client.h b/kopete/protocols/groupwise/libgroupwise/client.h new file mode 100644 index 00000000..102b0c27 --- /dev/null +++ b/kopete/protocols/groupwise/libgroupwise/client.h @@ -0,0 +1,401 @@ +/* + Kopete Groupwise Protocol + client.h - The main interface for the Groupwise protocol + + Copyright (c) 2004 SUSE Linux AG http://www.suse.com + + Based on Iris, Copyright (C) 2003 Justin Karneges + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#ifndef LIBGW_CLIENT_H +#define LIBGW_CLIENT_H + +#include <qstring.h> + +#include "gwclientstream.h" +#include "gwerror.h" +#include "rtf2html.h" +#include "transfer.h" + +class ChatroomManager; +class PrivacyManager; +class RequestFactory; +class UserDetailsManager; +class Task; + +using namespace GroupWise; + +class Client : public QObject +{ +Q_OBJECT + + public: + + /************* + EXTERNAL API + *************/ + + Client( QObject *parent = 0, uint protocolVersion = 2 ); + ~Client(); + void setOSName( const QString &name ); + void setClientName( const QString &s ); + void setClientVersion( const QString &s ); + void setUserDN( const QString & userDN ); + /** + * Start a connection to the server using the supplied @ref ClientStream. + * This is only a transport layer connection. + * Needed for protocol action P1. + * @param s initialised client stream to use for the connection. + * @param server the server to connect to - but this is also set on the connector used to construct the clientstream?? + * @param auth needed for jabber protocol layer only? + */ + void connectToServer( ClientStream *s, const NovellDN &server, bool auth=true ); + + /** + * Login to the GroupWise server using the supplied credentials + * Protocol action P1, needed for all + * @param host - probably could obtain this back from the connector - used for outgoing tasks to determine destination + * @param user The user name to log in as. +fd * @param password + */ + void start( const QString &host, const uint port, const QString &userId, const QString &pass ); + + /** + * Logout and disconnect + * Protocol action P4 void distribute(const QDomElement &); + + */ + void close(); + + /** + * Accessors needed for login + */ + QString host(); + int port(); + + /** + * Set the user's presence on the server + * Protocol action P2 + * @param status status enum + * @param reason custom status name for away statuses + * @param autoReply auto reply message for use in this status + */ + void setStatus( GroupWise::Status status, const QString & reason, const QString & autoReply ); + + /** + * Send a message + * Protocol action P10 + * @param message contains the text and the recipient. + */ + void sendMessage( const QStringList & addresseeDNs, const OutgoingMessage & message ); + + /** + * Send a typing notification + * Protocol action P11 + * @param conference The conference where the typing took place. + * @param typing True if the user is now typing, false otherwise. + */ + void sendTyping( const ConferenceGuid & conferenceGuid, bool typing ); + + /** + * Request details for one or more users, for example, if we receive a message from someone who isn't on our contact list + * @param userDNs A list of one or more user's DNs to fetch details for + */ + void requestDetails( const QStringList & userDNs ); + + /** + * Request the status of a single user, for example, if they have messaged us and are not on our contact list + */ + void requestStatus( const QString & userDN ); + + /** + * Add a contact to the contact list + * Protocol action P12 + */ + + /** + * Remove a contact from the contact list + * Protocol action P13 + */ + + /** + * Instantiate a conference on the server + * Protocol action P5 + */ + void createConference( const int clientId ); + /** + * Overloaded version of the above to create a conference with a supplied list of invitees + */ + void createConference( const int clientId, const QStringList & participants ); + + /** + * Join a conference, accepting an invitation + * Protocol action P7 + */ + void joinConference( const ConferenceGuid & guid ); + + /** + * Reject a conference invitation + * Protocol action P8 + */ + void rejectInvitation( const ConferenceGuid & guid ); + + /** + * Leave a conference, notifying + * Protocol action P6 + */ + void leaveConference( const ConferenceGuid & guid ); + + /** + * Send an invitation to join a conference + * Protocol action P9 + */ + void sendInvitation( const ConferenceGuid & guid, const QString & dn, const GroupWise::OutgoingMessage & message ); + /************* + INTERNAL (FOR USE BY TASKS) METHODS + *************/ + /** + * Send an outgoing request to the server + */ + void send( Request *request ); + /** + * Print a debug statement + */ + void debug( const QString &str ); + + /** + * The protocol version of the Client + */ + uint protocolVersion() const; + /** + * Generate a unique ID for Tasks. + */ + QString genUniqueId(); + + /** + * The current user's user ID + */ + QString userId(); + + /** + * The current user's DN + */ + QString userDN(); + /** + * The current user's password + */ + QString password(); + + /** + * User agent details for this host + */ + QString userAgent(); + + /** + * Host's IP address + */ + QCString ipAddress(); + + /** + * Obtain the list of custom statuses stored on the server + */ + QValueList<GroupWise::CustomStatus> customStatuses(); + + /** + * Get a reference to the RequestFactory for this Client. + * Used by Tasks to generate Requests with an ascending sequence of transaction IDs + * for this connection + */ + RequestFactory * requestFactory(); + + /** + * Get a reference to the ChatroomManager for this Client. + * This is constructed the first time this function is called. Used to manipulate chat rooms on the server. + */ + ChatroomManager * chatroomManager(); + + /** + * Get a reference to the UserDetailsManager for this Client. + * Used to track known user details and issue new details requests + */ + UserDetailsManager * userDetailsManager(); + /** + * Get a reference to the PrivacyManager for this Client. + * Used to track and manipulate server side privacy settings + */ + PrivacyManager * privacyManager(); + /** + * Access the root Task for this client, so tasks may be added to it. + */ + Task* rootTask(); + + signals: + /** CONNECTION EVENTS */ + /** + * Notifies that the login process has succeeded. + */ + void loggedIn(); + void loginFailed(); + /** + * Notifies tasks and account so they can react properly + */ + void disconnected(); + /** + * We were disconnected because we connected elsewhere + */ + void connectedElsewhere(); + + /** STATUS AND METADATA EVENTS */ + /** + * We've just got the user's own details from the server. + */ + void accountDetailsReceived( const GroupWise::ContactDetails & ); + /** + * We've just found out about a folder from the server. + */ + void folderReceived( const FolderItem & ); + /** + * We've just found out about a folder from the server. + */ + void contactReceived( const ContactItem & ); + /** + * We've just received a contact's metadata from the server. + */ + void contactUserDetailsReceived( const GroupWise::ContactDetails & ); + /** + * A remote contact changed status + */ + void statusReceived( const QString & contactId, Q_UINT16 status, const QString & statusText ); + /** + * Our status changed on the server + */ + void ourStatusChanged( GroupWise::Status status, const QString & statusText, const QString & autoReply ); + + /** CONFERENCE (MANAGEMENT) EVENTS */ + /** + * Notify that we've just received a message. Sender may not be on our contact list + */ + void messageReceived( const ConferenceEvent & ); + /** + * Notify that we've received an auto reply. This Event does not contain any rtf, unlike a normal message. + */ + void autoReplyReceived( const ConferenceEvent & ); + /** + * A conference was successfully created on the server + */ + void conferenceCreated( const int clientId, const GroupWise::ConferenceGuid & guid ); + /** + * A third party was invited to join a chat. They may not be on our contact list. + */ + void inviteNotifyReceived( const ConferenceEvent & ); + /** + * We were invited to join a chat. The inviter may not be on our contact list + */ + void invitationReceived( const ConferenceEvent & ); + /** + * Someone joined a chat. They may not be on our contact list if it is a group chat + * and they were invited to join the chat prior to our being invited to join and joining + */ + void conferenceJoinNotifyReceived( const ConferenceEvent & ); + /** + * Someone left a conference. This may close a conference, see @ref conferenceClosed. + */ + void conferenceLeft( const ConferenceEvent & ); + /** + * Someone declined an invitation to join a conference. This may close a conference, see @ref conferenceClosed. + */ + void invitationDeclined( const ConferenceEvent & ); + /** + * A conference was closed by the server. This occurs if we are the only participant and there + * are no outstanding invitations. + */ + void conferenceClosed( const ConferenceEvent & ); + /** + * We joined a conference. + */ + void conferenceJoined( const GroupWise::ConferenceGuid &, const QStringList &, const QStringList & ); + /** + * We received an "is typing" event in a conference + */ + void contactTyping( const ConferenceEvent & ); + /** + * We received an "is not typing event" in a conference + */ + void contactNotTyping( const ConferenceEvent & ); + /** + * An attempt to create a conference failed. + */ + void conferenceCreationFailed( const int clientId, const int error ); + /** + * We received a temporary contact related to a conference + */ + void tempContactReceived( const GroupWise::ContactDetails & ); + /** + * We received a broadcast message + */ + void broadcastReceived( const ConferenceEvent & ); + /** + * We received a system broadcast + */ + void systemBroadcastReceived ( const ConferenceEvent & ); + /** CONTACT LIST MANAGEMENT EVENTS */ + /** TBD! */ + void messageSendingFailed(); + protected: + /** + * Instantiate all the event handling tasks + */ + void initialiseEventTasks(); + protected slots: + // INTERNAL, FOR USE BY TASKS' finished() SIGNALS // + void lt_loginFinished(); + void sst_statusChanged(); + void cct_conferenceCreated(); + /** + * Transforms an RTF message into an HTML message and emits messageReceived() + */ + void ct_messageReceived( const ConferenceEvent & ); + void jct_joinConfCompleted(); + /** + * Receive a custom status during login and record it + */ + void lt_gotCustomStatus( const GroupWise::CustomStatus & ); + /** + * Notify us of the keepalive period contained in the login response + */ + void lt_gotKeepalivePeriod( int ); + + /** + * Used by the client stream to notify errors to upper layers. + */ + void streamError( int error ); + + /** + * The client stream has data ready to read. + */ + void streamReadyRead(); + + /** + * sendout a 'ping' keepalive message so that the server does not disconnect us + */ + void sendKeepAlive(); + void smt_messageSent(); + + private: + void distribute( Transfer *transfer ); + class ClientPrivate; + ClientPrivate* d; +}; + +#endif diff --git a/kopete/protocols/groupwise/libgroupwise/connector.cpp b/kopete/protocols/groupwise/libgroupwise/connector.cpp new file mode 100644 index 00000000..55e866ee --- /dev/null +++ b/kopete/protocols/groupwise/libgroupwise/connector.cpp @@ -0,0 +1,73 @@ +/* + Kopete Groupwise Protocol + connector.cpp - the Groupwise socket connector + + Copyright (c) 2004 SUSE Linux AG http://www.suse.com + + Based on Iris, Copyright (C) 2003 Justin Karneges + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#include "connector.h" + +Connector::Connector(QObject *parent) +:QObject(parent) +{ + setUseSSL(false); + setPeerAddressNone(); +} + +Connector::~Connector() +{ +} + +bool Connector::useSSL() const +{ + return ssl; +} + +bool Connector::havePeerAddress() const +{ + return haveaddr; +} + +QHostAddress Connector::peerAddress() const +{ + return addr; +} + +Q_UINT16 Connector::peerPort() const +{ + return port; +} + +void Connector::setUseSSL(bool b) +{ + ssl = b; +} + +void Connector::setPeerAddressNone() +{ + haveaddr = false; + addr = QHostAddress(); + port = 0; +} + +void Connector::setPeerAddress(const QHostAddress &_addr, Q_UINT16 _port) +{ + haveaddr = true; + addr = _addr; + port = _port; +} + +#include "connector.moc" diff --git a/kopete/protocols/groupwise/libgroupwise/connector.h b/kopete/protocols/groupwise/libgroupwise/connector.h new file mode 100644 index 00000000..1cae3426 --- /dev/null +++ b/kopete/protocols/groupwise/libgroupwise/connector.h @@ -0,0 +1,61 @@ +/* + Kopete Groupwise Protocol + connector.h - the Groupwise socket connector + + Copyright (c) 2004 SUSE Linux AG http://www.suse.com + + Based on Iris, Copyright (C) 2003 Justin Karneges + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#ifndef LIBGW_CONNECTOR_H +#define LIBGW_CONNECTOR_H + +#include <qhostaddress.h> +#include <qobject.h> + +class ByteStream; + +class Connector : public QObject +{ + Q_OBJECT +public: + Connector(QObject *parent=0); + virtual ~Connector(); + + virtual void connectToServer(const QString &server)=0; + virtual ByteStream *stream() const=0; + virtual void done()=0; + + bool useSSL() const; + bool havePeerAddress() const; + QHostAddress peerAddress() const; + Q_UINT16 peerPort() const; + +signals: + void connected(); + void error(); + +protected: + void setUseSSL(bool b); + void setPeerAddressNone(); + void setPeerAddress(const QHostAddress &addr, Q_UINT16 port); + +private: + bool ssl; + bool haveaddr; + QHostAddress addr; + Q_UINT16 port; +}; + +#endif diff --git a/kopete/protocols/groupwise/libgroupwise/coreprotocol.cpp b/kopete/protocols/groupwise/libgroupwise/coreprotocol.cpp new file mode 100644 index 00000000..1e15287e --- /dev/null +++ b/kopete/protocols/groupwise/libgroupwise/coreprotocol.cpp @@ -0,0 +1,507 @@ +/* + Kopete Groupwise Protocol + coreprotocol.h- the core GroupWise protocol + + Copyright (c) 2004 SUSE Linux AG http://www.suse.com + + Based on Iris, Copyright (C) 2003 Justin Karneges + url_escape_string from Gaim src/protocols/novell/nmconn.c + Copyright (c) 2004 Novell, Inc. All Rights Reserved + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#include <string.h> +#include <iostream> +#include <stdlib.h> + +#include <qdatastream.h> +#include <qdatetime.h> +#include <qtextstream.h> + + +#include <kdebug.h> +#include <kurl.h> + +#include "eventprotocol.h" +#include "eventtransfer.h" +#include "gwerror.h" +#include "gwfield.h" +#include "request.h" +#include "response.h" +#include "responseprotocol.h" + +#include "coreprotocol.h" + +#define NO_ESCAPE(ch) ((ch == 0x20) || (ch >= 0x30 && ch <= 0x39) || (ch >= 0x41 && ch <= 0x5a) || (ch >= 0x61 && ch <= 0x7a)) +#define GW_URLVAR_TAG "&tag=" +#define GW_URLVAR_METHOD "&cmd=" +#define GW_URLVAR_VAL "&val=" +#define GW_URLVAR_TYPE "&type=" + +//#define GW_COREPROTOCOL_DEBUG + +QCString +url_escape_string( const char *src) +{ + uint escape = 0; + const char *p; + uint q; + //char *encoded = NULL; + int ch; + + static const char hex_table[17] = "0123456789abcdef"; + + if (src == NULL) { + return QCString(); + } + + /* Find number of chars to escape */ + for (p = src; *p != '\0'; p++) { + ch = (uchar) *p; + if (!NO_ESCAPE(ch)) { + escape++; + } + } + + QCString encoded((p - src) + (escape * 2) + 1); + + /* Escape the string */ + for (p = src, q = 0; *p != '\0'; p++) { + ch = (uchar) * p; + if (NO_ESCAPE(ch)) { + if (ch != 0x20) { + encoded.insert( q, (char)ch ); + q++; + } else { + encoded.insert( q, '+' ); + q++; + } + } else { + encoded.insert( q, '%' ); + q++; + + encoded.insert( q, hex_table[ch >> 4] ); + q++; + + encoded.insert( q, hex_table[ch & 15] ); + q++; + } + } + encoded.insert( q, '\0' ); + + return encoded; +} + +CoreProtocol::CoreProtocol() : QObject() +{ + m_eventProtocol = new EventProtocol( this, "eventprotocol" ); + m_responseProtocol = new ResponseProtocol( this, "responseprotocol" ); +} + +CoreProtocol::~CoreProtocol() +{ +} + +int CoreProtocol::state() +{ + return m_state; +} + +void CoreProtocol::debug( const QString &str ) +{ +#ifdef LIBGW_USE_KDEBUG + kdDebug( 14191 ) << "debug: " << str << endl; +#else + qDebug( "GW RAW PROTO: %s\n", str.ascii() ); +#endif +} + +void CoreProtocol::addIncomingData( const QByteArray & incomingBytes ) +{ +// store locally + debug( "CoreProtocol::addIncomingData()"); + int oldsize = m_in.size(); + m_in.resize( oldsize + incomingBytes.size() ); + memcpy( m_in.data() + oldsize, incomingBytes.data(), incomingBytes.size() ); + m_state = Available; + // convert every event in the chunk to a Transfer, signalling it back to the clientstream + + int parsedBytes = 0; + int transferCount = 0; + // while there is data left in the input buffer, and we are able to parse something out of it + while ( m_in.size() && ( parsedBytes = wireToTransfer( m_in ) ) ) + { + transferCount++; + debug( QString( "CoreProtocol::addIncomingData() - parsed transfer #%1 in chunk" ).arg( transferCount ) ); + int size = m_in.size(); + if ( parsedBytes < size ) + { + debug( " - more data in chunk!" ); + // copy the unparsed bytes into a new qbytearray and replace m_in with that + QByteArray remainder( size - parsedBytes ); + memcpy( remainder.data(), m_in.data() + parsedBytes, remainder.size() ); + m_in = remainder; + } + else + m_in.truncate( 0 ); + } + if ( m_state == NeedMore ) + debug( " - message was incomplete, waiting for more..." ); + if ( m_eventProtocol->state() == EventProtocol::OutOfSync ) + { + debug( " - protocol thinks it's out of sync, discarding the rest of the buffer and hoping the server regains sync soon..." ); + m_in.truncate( 0 ); + } + debug( " - done processing chunk" ); +} + +Transfer* CoreProtocol::incomingTransfer() +{ + debug( "CoreProtocol::incomingTransfer()" ); + if ( m_state == Available ) + { + debug( " - got a transfer" ); + m_state = NoData; + return m_inTransfer; + m_inTransfer = 0; + } + else + { + debug( " - no milk today." ); + return 0; + } +} + +void cp_dump( const QByteArray &bytes ) +{ +#ifdef LIBGW_DEBUG + CoreProtocol::debug( QString( "contains: %1 bytes" ).arg( bytes.count() ) ); + for ( uint i = 0; i < bytes.count(); ++i ) + { + printf( "%02x ", bytes[ i ] ); + } + printf( "\n" ); +#else + Q_UNUSED(bytes); +#endif +} + +void CoreProtocol::outgoingTransfer( Request* outgoing ) +{ + debug( "CoreProtocol::outgoingTransfer()" ); + // Convert the outgoing data into wire format + Request * request = dynamic_cast<Request *>( outgoing ); + Field::FieldList fields = request->fields(); + if ( fields.isEmpty() ) + { + debug( " CoreProtocol::outgoingTransfer: Transfer contained no fields, it must be a ping."); +/* m_error = NMERR_BAD_PARM; + return;*/ + } + // Append field containing transaction id + Field::SingleField * fld = new Field::SingleField( NM_A_SZ_TRANSACTION_ID, NMFIELD_METHOD_VALID, + 0, NMFIELD_TYPE_UTF8, request->transactionId() ); + fields.append( fld ); + + // convert to QByteArray + QByteArray bytesOut; + QTextStream dout( bytesOut, IO_WriteOnly ); + dout.setEncoding( QTextStream::Latin1 ); + //dout.setByteOrder( QDataStream::LittleEndian ); + + // strip out any embedded host and port in the command string + QCString command, host, port; + if ( request->command().section( ':', 0, 0 ) == "login" ) + { + command = "login"; + host = request->command().section( ':', 1, 1 ).ascii(); + port = request->command().section( ':', 2, 2 ).ascii(); + debug( QString( "Host: %1 Port: %2" ).arg( host.data() ).arg( port.data() ) ); + } + else + command = request->command().ascii(); + + // add the POST + dout << "POST /"; + dout << command; + dout << " HTTP/1.0\r\n"; + + // if a login, add Host arg + if ( command == "login" ) + { + dout << "Host: "; + dout << host; //FIXME: Get this from somewhere else!! + dout << ":" << port << "\r\n\r\n"; + } + else + dout << "\r\n"; + + debug( QString( "data out: %1" ).arg( bytesOut.data() ) ); + + emit outgoingData( bytesOut ); + // now convert + fieldsToWire( fields ); + delete request; + delete fld; + return; +} + +void CoreProtocol::fieldsToWire( Field::FieldList fields, int depth ) +{ + debug( "CoreProtocol::fieldsToWire()"); + int subFieldCount = 0; + + // TODO: consider constructing this as a QStringList and then join()ing it. + Field::FieldListIterator it; + Field::FieldListIterator end = fields.end(); + Field::FieldBase* field; + for ( it = fields.begin(); it != end ; ++it ) + { + field = *it; + //debug( " - writing a field" ); + QByteArray bytesOut; + QDataStream dout( bytesOut, IO_WriteOnly ); + dout.setByteOrder( QDataStream::LittleEndian ); + + // these fields are ignored by Gaim's novell + if ( field->type() == NMFIELD_TYPE_BINARY || field->method() == NMFIELD_METHOD_IGNORE ) + continue; + + // GAIM writes these tags to the secure socket separately - if we can't connect, check here + // NM Protocol 1 writes them in an apparently arbitrary order + // tag + //dout.writeRawBytes( GW_URLVAR_TAG, sizeof( GW_URLVAR_TAG ) ); + //dout << field->tag(); + + // method + //dout.writeRawBytes( GW_URLVAR_METHOD, sizeof( GW_URLVAR_METHOD ) ); + // char methodChar = encode_method( field->method() ); + //dout << (Q_UINT8)methodChar; + + // value + //dout.writeRawBytes( GW_URLVAR_VAL, sizeof( GW_URLVAR_VAL ) ); + + char valString[ NMFIELD_MAX_STR_LENGTH ]; + switch ( field->type() ) + { + case NMFIELD_TYPE_UTF8: // Field contains UTF-8 + case NMFIELD_TYPE_DN: // Field contains a Distinguished Name + { + //debug( " - it's a single string" ); + const Field::SingleField *sField = static_cast<const Field::SingleField*>( field ); +// QString encoded = KURL::encode_string( sField->value().toString(), 106 /* UTF-8 */); +// encoded.replace( "%20", "+" ); +// dout << encoded.ascii(); + + snprintf( valString, NMFIELD_MAX_STR_LENGTH, "%s", url_escape_string( sField->value().toString().utf8() ).data() ); + //dout << sField->value().toString().ascii(); + break; + } + case NMFIELD_TYPE_ARRAY: // Field contains a field array + case NMFIELD_TYPE_MV: // Field contains a multivalue + { + //debug( " - it's a multi" ); + const Field::MultiField *mField = static_cast<const Field::MultiField*>( field ); + subFieldCount = mField->fields().count(); // determines if we have a subarray to send after this field + //dout << QString::number( subFieldCount ).ascii(); + snprintf( valString, NMFIELD_MAX_STR_LENGTH, "%u", subFieldCount ); + break; + } + default: // Field contains a numeric value + { + //debug( " - it's a number" ); + const Field::SingleField *sField = static_cast<const Field::SingleField*>( field ); + //dout << QString::number( sField->value().toInt() ).ascii(); + snprintf( valString, NMFIELD_MAX_STR_LENGTH, "%u", sField->value().toInt() ); + } + } + + // type + //dout.writeRawBytes( GW_URLVAR_TYPE, sizeof( GW_URLVAR_TYPE ) ); + + //dout << QString::number( field->type() ).ascii(); + QCString typeString; + typeString.setNum( field->type() ); + QCString outgoing = GW_URLVAR_TAG + field->tag() + + GW_URLVAR_METHOD + (char)encode_method( field->method() ) + + GW_URLVAR_VAL + (const char *)valString + + GW_URLVAR_TYPE + typeString; + + debug( QString( "CoreProtocol::fieldsToWire - outgoing data: %1" ).arg( outgoing.data() ) ); + dout.writeRawBytes( outgoing.data(), outgoing.length() ); + // write what we have so far, we may be calling this function recursively + //kdDebug( 14999 ) << k_funcinfo << "writing \'" << bout << "\'" << endl; + //debug( " - signalling data" ); + emit outgoingData( bytesOut ); + + // write fields of subarray, if that's what the current field is + if ( subFieldCount > 0 && + ( field->type() == NMFIELD_TYPE_ARRAY || field->type() == NMFIELD_TYPE_MV ) ) + { + const Field::MultiField *mField = static_cast<const Field::MultiField*>( field ); + fieldsToWire( mField->fields(), depth + 1 ); + } + //debug( " - field done" ); + } + if ( depth == 0 ) // this call to the function was not recursive, so the entire request has been sent at this point + { + // very important, don't send put the \r\n on the wire as a string or it will be preceded with the string length and 0 terminated, which the server reads as a request to disconnect. + QByteArray bytesOut; + QDataStream dout( bytesOut, IO_WriteOnly ); + dout.setByteOrder( QDataStream::LittleEndian ); + dout.writeRawBytes( "\r\n", 2 ); + emit outgoingData( bytesOut ); + debug( "CoreProtocol::fieldsToWire - request completed" ); + } + //debug( " - method done" ); +} + +int CoreProtocol::wireToTransfer( const QByteArray& wire ) +{ + // processing incoming data and reassembling it into transfers + // may be an event or a response + uint bytesParsed = 0; + m_din = new QDataStream( wire, IO_ReadOnly ); + m_din->setByteOrder( QDataStream::LittleEndian ); + + // look at first four bytes and decide what to do with the chunk + Q_UINT32 val; + if ( okToProceed() ) + { + *m_din >> val; + + // if is 'HTTP', it's a Response. PTTH it is after endian conversion + if ( !qstrncmp( (const char *)&val, "HTTP", strlen( "HTTP" ) ) || + !qstrncmp( (const char *)&val, "PTTH", strlen( "PTTH" ) ) + ) { + Transfer * t = m_responseProtocol->parse( wire, bytesParsed ); + if ( t ) + { + m_inTransfer = t; + debug( "CoreProtocol::wireToTransfer() - got a RESPONSE " ); + + m_state = Available; + emit incomingData(); + } + else + bytesParsed = 0; + } + else // otherwise -> Event code + { + debug( QString( "CoreProtocol::wireToTransfer() - looks like an EVENT: %1, length %2" ).arg( val ).arg( wire.size() ) ); + Transfer * t = m_eventProtocol->parse( wire, bytesParsed ); + if ( t ) + { + m_inTransfer = t; + debug( QString( "CoreProtocol::wireToTransfer() - got an EVENT: %1, parsed: %2" ).arg( val ).arg( bytesParsed ) ); + m_state = Available; + emit incomingData(); + } + else + { + debug( "CoreProtocol::wireToTransfer() - EventProtocol was unable to parse it" ); + bytesParsed = 0; + } + } + } + delete m_din; + return bytesParsed; +} + +void CoreProtocol::reset() +{ + m_in.resize( 0 ); +} + +QChar CoreProtocol::encode_method( Q_UINT8 method ) +{ + QChar str; + + switch (method) { + case NMFIELD_METHOD_EQUAL: + str = 'G'; + break; + case NMFIELD_METHOD_UPDATE: + str = 'F'; + break; + case NMFIELD_METHOD_GTE: + str = 'E'; + break; + case NMFIELD_METHOD_LTE: + str = 'D'; + break; + case NMFIELD_METHOD_NE: + str = 'C'; + break; + case NMFIELD_METHOD_EXIST: + str = 'B'; + break; + case NMFIELD_METHOD_NOTEXIST: + str = 'A'; + break; + case NMFIELD_METHOD_SEARCH: + str = '9'; + break; + case NMFIELD_METHOD_MATCHBEGIN: + str = '8'; + break; + case NMFIELD_METHOD_MATCHEND: + str = '7'; + break; + case NMFIELD_METHOD_NOT_ARRAY: + str = '6'; + break; + case NMFIELD_METHOD_OR_ARRAY: + str = '5'; + break; + case NMFIELD_METHOD_AND_ARRAY: + str = '4'; + break; + case NMFIELD_METHOD_DELETE_ALL: + str = '3'; + break; + case NMFIELD_METHOD_DELETE: + str = '2'; + break; + case NMFIELD_METHOD_ADD: + str = '1'; + break; + default: /* NMFIEL D_METHOD_VALID */ + str = '0'; + break; + } + + return str; +} + +void CoreProtocol::slotOutgoingData( const QCString &out ) +{ + debug( QString( "CoreProtocol::slotOutgoingData() %1" ).arg( out ) ); +} + +bool CoreProtocol::okToProceed() +{ + if ( m_din ) + { + if ( m_din->atEnd() ) + { + m_state = NeedMore; + debug( "CoreProtocol::okToProceed() - Server message ended prematurely!" ); + } + else + return true; + } + return false; +} + +#include "coreprotocol.moc" diff --git a/kopete/protocols/groupwise/libgroupwise/coreprotocol.h b/kopete/protocols/groupwise/libgroupwise/coreprotocol.h new file mode 100644 index 00000000..4cd30b88 --- /dev/null +++ b/kopete/protocols/groupwise/libgroupwise/coreprotocol.h @@ -0,0 +1,202 @@ +/* + Kopete Groupwise Protocol + coreprotocol.h- the core GroupWise protocol + + Copyright (c) 2004 SUSE Linux AG http://www.suse.com + + Based on Iris, Copyright (C) 2003 Justin Karneges + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#ifndef GW_CORE_PROTOCOL_H +#define GW_CORE_PROTOCOL_H + +#include <qcstring.h> +#include <qobject.h> +#include <qptrlist.h> + +#include "gwfield.h" + +class EventProtocol; +class ResponseProtocol; +class Request; +class Transfer; + +/** + * This class handles transforming data between structured high level messages and encoded bytes that are sent + * and received over the network. + * + * 0) FIELD ARRAYS + * --------------- + * This is relevant to both input and output handling. + * Requests (out) and Responses (in) are messages containing, after a HTTP header, a series of 'Fields'. + * A message may contain a flat series of Fields, or each Field may mark the start of a nested array of more Fields. + * In this case the Field's value is the length of the following nested array. + * The length of the top level Field series is not given. The message ends when there are no more Fields expected as part of a nested array, + * and is marked by a terminator. + * The encoding used for Fields differs for Requests and Responses, and is described below. + * + * 1) INPUT + * -------- + * The input functionality is a finite state machine that processes the stream of data from the GroupWise server. + * Since the server may arbitrarily truncate or run together protocol level messages, we buffer the incoming data stream, + * parsing it into individual messages that are removed from the buffer and passed back to the ClientStream, which propagates + * them to higher layers. + * + * Incoming data may be in either of two formats; a Response or an Event. + * All binary data is Little Endian on the network. + * + * 1.1) INPUT MESSAGE 'SPECIES' + * + * 1.1.1) Events + * + * Events are independently occuring notifications generated by the server or by the activity of other users. + * Events are represented on the wire in binary format: + * + * BYTE 1 + * 0 8 6.... + * AAAABBBBCCCCCCCCC....DDDDDDDD..... + * AAAA is a UINT32 giving the type of event + * BBBB is a UINT32 giving the length of the event source, + * CCCC... is the event source, a UTF8 encoded string, which is observed to be zero terminated + * DDDD... is event dependent binary data, which frequently consists of the conference the event relates to, + * conference flags describing the logging, chat security and closed status, and message data. + * + * As the DDDD portion is irregularly structured, it must be processed knowing the semantics of the event type. + * See the @ref EventProtocol documentation. + * + * Event message data is always a UINT32 giving the message length, then a message in RTF format. + * The message length may be zero. + * + * 1.1.2) Responses + * Responses are the server's response to client Requests. Each Request generates one Response. Requests and Responses are regularly structured + * and can be parsed/generated without any knowledge of their content. + * Responses consist of text/line oriented standard HTTP headers, followed by a binary payload. The payload is a series of Fields as described above, + * and the terminator following the last field is a null (0x0) byte. + * + * TODO: Add Field structure format: type, tag, method, flags, and value. see ResponseProtocol::readFields() for reference if this is incomplete. + * + * 1.3) INPUT PROCESSING IMPLEMENTATION + * CoreProtocol input handling operates on an event driven basis. It starts processing when it receives data via @ref addIncomingData(), + * and emits @ref incomingData() as each complete message is parsed in off the wire. + * Each call to addIncomingData() may result in zero or more incomingData() signals + * + * 2) REQUESTS + * ----------- + * The output functionality is an encoding function that transforms outgoing Requests into the wire request format + * - a HTTP POST made up of the request operation type as the path, followed by a series of (repeated) variables that form the arguments. + * Order of the arguments is significant! + * Argument values are URL-encoded with spaces encoded as + rather than %20. + * The terminator used is a CRLF pair ("\r\n"). + * HTTP headers are only used in a login operation, where they contain a Host: hostname:port line. + * Headers are separated from the arguments by a blank line (only CRLF) as usual. + * + * 3) USER MESSAGE BODY TEXT REPRESENTATION + * ----------------------------------- + * Message text sent by users (found in both Requests and Events) is generally formatted as Rich Text Format. + * Text portions of the RTF may be be encoded in + * any of three ways - + * ascii text, + * latin1 as hexadecimal, + * escaped unicode code points (encoded/escaped as \uUNICODEVALUE?, with or without a space between the end of the unicode value and the ? ) + * Outgoing messages may contain rich text, and additionally the plain text encoded as UTF8, but this plain payload is apparently ignored by the server + * + */ +class CoreProtocol : public QObject +{ +Q_OBJECT +public: + enum State { NeedMore, Available, NoData }; + + CoreProtocol(); + + virtual ~CoreProtocol(); + /** + * Debug output + */ + static void debug(const QString &str); + + /** + * Reset the protocol, clear buffers + */ + void reset(); + + /** + * Accept data from the network, and buffer it into a useful message + * @param incomingBytes Raw data in wire format. + */ + void addIncomingData( const QByteArray& incomingBytes ); + + /** + * @return the incoming transfer or 0 if none is available. + */ + Transfer* incomingTransfer(); + + /** + * Convert a request into an outgoing transfer + * emits @ref outgoingData() with each part of the transfer + */ + void outgoingTransfer( Request* outgoing ); + + /** + * Get the state of the protocol + */ + int state(); + +signals: + /** + * Emitted as the core protocol converts fields to wire ready data + */ + void outgoingData( const QByteArray& ); + /** + * Emitted when there is incoming data, parsed into a Transfer + */ + void incomingData(); +protected slots: + /** + * Just a debug method to test emitting to the socket, atm - should go to the ClientStream + */ + void slotOutgoingData( const QCString & ); + +protected: + /** + * Check that there is data to read, and set the protocol's state if there isn't any. + */ + bool okToProceed(); + /** + * Convert incoming wire data into a Transfer object and queue it + * @return number of bytes from the input that were parsed into a Transfer + */ + int wireToTransfer( const QByteArray& wire ); + /** + * Convert fields to a wire representation. Emits outgoingData as each field is written. + * Calls itself recursively to process nested fields, hence + * @param depth Current depth of recursion. Don't use this parameter yourself! + */ + void fieldsToWire( Field::FieldList fields, int depth = 0 ); + /** + * encodes a method number (usually supplied as a #defined symbol) to a char + */ + QChar encode_method( Q_UINT8 method ); +private: + QByteArray m_in; // buffer containing unprocessed bytes we received + QDataStream* m_din; // contains the packet currently being parsed + int m_error; + Transfer* m_inTransfer; // the transfer that is being received + int m_state; // represents the protocol's overall state + EventProtocol* m_eventProtocol; + ResponseProtocol * m_responseProtocol; +}; + +#endif + diff --git a/kopete/protocols/groupwise/libgroupwise/eventprotocol.cpp b/kopete/protocols/groupwise/libgroupwise/eventprotocol.cpp new file mode 100644 index 00000000..05320676 --- /dev/null +++ b/kopete/protocols/groupwise/libgroupwise/eventprotocol.cpp @@ -0,0 +1,216 @@ +/* + Kopete Groupwise Protocol + eventprotocol.cpp - reads the protocol used by GroupWise for signalling Events + + Copyright (c) 2004 SUSE Linux AG http://www.suse.com + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#include <qbuffer.h> + +#include "gwerror.h" + +#include "eventtransfer.h" +#include "eventprotocol.h" + +using namespace GroupWise; + +EventProtocol::EventProtocol(QObject *parent, const char *name) + : InputProtocolBase(parent, name) +{ +} + +EventProtocol::~EventProtocol() +{ +} + +Transfer * EventProtocol::parse( const QByteArray & wire, uint& bytes ) +{ + m_bytes = 0; + //m_din = new QDataStream( wire, IO_ReadOnly ); + QBuffer inBuf( wire ); + inBuf.open( IO_ReadOnly); + m_din.setDevice( &inBuf ); + m_din.setByteOrder( QDataStream::LittleEndian ); + Q_UINT32 type; + + if ( !okToProceed() ) + { + m_din.unsetDevice(); + return 0; + } + // read the event type + m_din >> type; + m_bytes += sizeof( Q_UINT32 ); + + debug( QString( "EventProtocol::parse() Reading event of type %1" ).arg( type ) ); + if ( type > Stop ) + { + debug( QString ( "EventProtocol::parse() - found unexpected event type %1 - assuming out of sync" ).arg( type ) ); + m_state = OutOfSync; + return 0; + } + + // read the event source + QString source; + if ( !readString( source ) ) + { + m_din.unsetDevice(); + return 0; + } + + // now create an event object + //HACK: lowercased DN + EventTransfer * tentative = new EventTransfer( type, source.lower(), QDateTime::currentDateTime() ); + + // add any additional data depending on the type of event + // Note: if there are any errors in the way the data is read below, we will soon be OutOfSync + QString statusText; + QString guid; + Q_UINT16 status; + Q_UINT32 flags; + QString message; + + switch ( type ) + { + case StatusChange: //103 - STATUS + STATUSTEXT + if ( !okToProceed() ) + { + m_din.unsetDevice(); + return 0; + } + m_din >> status; + m_bytes += sizeof( Q_UINT16 ); + if ( !readString( statusText ) ) + { + m_din.unsetDevice(); + return 0; + } + debug( QString( "got status: %1").arg( status ) ); + tentative->setStatus( status ); + debug( QString( "tentative status: %1").arg( tentative->status() ) ); + tentative->setStatusText( statusText ); + break; + case ConferenceJoined: // 106 - GUID + FLAGS + case ConferenceLeft: // 107 + if ( !readString( guid ) ) + { + m_din.unsetDevice(); + return 0; + } + tentative->setGuid( guid ); + if ( !readFlags( flags ) ) + { + m_din.unsetDevice(); + return 0; + } + tentative->setFlags( flags ); + break; + case UndeliverableStatus: //102 - GUID + case ConferenceClosed: //105 + case ConferenceInviteNotify://118 + case ConferenceReject: //119 + case UserTyping: //112 + case UserNotTyping: //113 + if ( !readString( guid ) ) + { + m_din.unsetDevice(); + return 0; + } + tentative->setGuid( guid ); + break; + case ReceiveAutoReply: //121 - GUID + FLAGS + MESSAGE + case ReceiveMessage: //108 + // guid + if ( !readString( guid ) ) + { + m_din.unsetDevice(); + return 0; + } + tentative->setGuid( guid ); + // flags + if ( !readFlags( flags ) ) + { + m_din.unsetDevice(); + return 0; + } + tentative->setFlags( flags ); + // message + if ( !readString( message ) ) + { + m_din.unsetDevice(); + return 0; + } + tentative->setMessage( message ); + break; + case ConferenceInvite: //117 GUID + MESSAGE + // guid + if ( !readString( guid ) ) + { + m_din.unsetDevice(); + return 0; + } + tentative->setGuid( guid ); + // message + if ( !readString( message ) ) + { + m_din.unsetDevice(); + return 0; + } + tentative->setMessage( message ); + break; + case UserDisconnect: //114 (NOTHING) + case ServerDisconnect: //115 + // nothing else to read + break; + case InvalidRecipient: //101 + case ContactAdd: //104 + case ReceiveFile: //109 + case ConferenceRename: //116 + // unhandled because unhandled in Gaim + break; + /* GW7 */ + case ReceivedBroadcast: //122 + case ReceivedSystemBroadcast: //123 + // message + if ( !readString( message ) ) + { + m_din.unsetDevice(); + return 0; + } + tentative->setMessage( message ); + break; + default: + debug( QString( "EventProtocol::parse() - found unexpected event type %1" ).arg( type ) ); + break; + } + // if we got this far, the parse succeeded, return the Transfer + m_state = Success; + //delete m_din; + bytes = m_bytes; + m_din.unsetDevice(); + return tentative; +} + +bool EventProtocol::readFlags( Q_UINT32 &flags) +{ + if ( okToProceed() ) + { + m_din >> flags; + m_bytes += sizeof( Q_UINT32 ); + return true; + } + return false; +} + +#include "eventprotocol.moc" diff --git a/kopete/protocols/groupwise/libgroupwise/eventprotocol.h b/kopete/protocols/groupwise/libgroupwise/eventprotocol.h new file mode 100644 index 00000000..4f6d94e5 --- /dev/null +++ b/kopete/protocols/groupwise/libgroupwise/eventprotocol.h @@ -0,0 +1,130 @@ +/* + Kopete Groupwise Protocol + eventprotocol.h - reads the protocol used by GroupWise for signalling Events + + Copyright (c) 2004 SUSE Linux AG http://www.suse.com + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#ifndef GW_EVENTPROTOCOL_H +#define GW_EVENTPROTOCOL_H + +#include "inputprotocolbase.h" + +class EventTransfer; +/** + * This class converts incoming event data into EventTransfer objects. Since it requires knowledge of the binary event format, which + * differs for each event type, it is implemented as a separate class. See also @ref CoreProtocol, which detects event messages in the + * data stream and hands them to this class for processing. + * Event Types + * +@author SUSE AG + Ablauf: + CoreProtocol receives data in addIncomingData, and passes to wireToTransfer. + wireToTransfer detects an event. + Passes whole chunk to EventProtocol ( as QByteArray ) + In to EventProtocol - QByteArray + Returned from EventProtocol - EventTransfer *, bytes read, set state? + EventProtocol tries to parse data into eventTransfer + If not complete, sets state to NeedMore and returns 0 + If complete, returns number of bytes read for the event + If bytes less than length of chunk, CoreProtocol::addIncomingData places the unread bytes back in m_in and calls wireToTransfer again. + if ResponseProtocol or EventProtocol set state to NeedMore, don't call wireToTransfer again. + + What event dependent data does EventTransfer contain? + + What if some events contain EXTRA bytes off the end that we don't know about? Then we will put those bytes back on the buffer, and try and parse those as the start of a new message!!! Need to react SAFELY then. + + What event dependent binary data does each event type contain? + + All Events contain an event code, and a source ( a DN ) + NOTHANDLED indicates that there is no further data and we don't handle events of that type, because they are not sent by the server + NONE indicates there is no further data + STATUSTEXT, GUID, MESSAGE indicate a string encoded in the usual GroupWise binary string encoding: a UINT32 containing the string length in little-endian, followed by the string itself, as UTF-8 encoded unicode. The string length value includes a terminating NUL, so when converting to a QString, subtract one from the string length. + FLAGS contains a UINT32 containing the server's flags for this conference. See gwerror.h for the possible values and meanings of these flags. Only Logging has been observed in practice. + + All events are timestamped with the local time on receipt. + + From gwerror.h: + enum Event { InvalidRecipient = 101, + NOTHANDLED + UndeliverableStatus = 102, + NOTHANDLED * + StatusChange = 103, + Q_UINT16 STATUS + STATUSTEXT + ContactAdd = 104, + NOTHANDLED + ConferenceClosed = 105, + GUID + ConferenceJoined = 106, + GUID + FLAGS + ConferenceLeft = 107, + GUID + FLAGS + ReceiveMessage = 108, + GUID + FLAGS + MESSAGE + ReceiveFile = 109, + NOTHANDLED + UserTyping = 112, + GUID + UserNotTyping = 113, + GUID + UserDisconnect = 114, + NONE + ServerDisconnect = 115, + NONE + ConferenceRename = 116, + NOTHANDLED + ConferenceInvite = 117, + GUID + MESSAGE + ConferenceInviteNotify = 118, + GUID + ConferenceReject = 119, + GUID + ReceiveAutoReply = 121, + GUID + FLAGS + MESSAGE + Start = InvalidRecipient, + Stop = ReceiveAutoReply + }; + Therefore we have GUID, FLAGS, MESSAGE, STATUS, STATUSTEXT. All transfers have TYPE and SOURCE, and a TIMESTAMP is added on receipt. +*/ + +class EventProtocol : public InputProtocolBase +{ +Q_OBJECT +public: + EventProtocol(QObject *parent = 0, const char *name = 0); + ~EventProtocol(); + /** + * Attempt to parse the supplied data into an @ref EventTransfer object. + * The exact state of the parse attempt can be read using @ref state. + * @param rawData The unparsed data. + * @param bytes An integer used to return the number of bytes read. + * @return A pointer to an EventTransfer object if successfull, otherwise 0. The caller is responsible for deleting this object. + */ + Transfer * parse( const QByteArray &, uint & bytes ); +protected: + /** + * Reads a conference's flags + */ + bool readFlags( Q_UINT32 &flags); +}; + +#endif diff --git a/kopete/protocols/groupwise/libgroupwise/eventtransfer.cpp b/kopete/protocols/groupwise/libgroupwise/eventtransfer.cpp new file mode 100644 index 00000000..06178f21 --- /dev/null +++ b/kopete/protocols/groupwise/libgroupwise/eventtransfer.cpp @@ -0,0 +1,145 @@ +/* + eventtransfer.cpp - Kopete Groupwise Protocol + + Copyright (c) 2004 SUSE Linux AG http://www.suse.com + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#include "eventtransfer.h" + +EventTransfer::EventTransfer( const Q_UINT32 eventType, const QString & source, QDateTime timeStamp ) + : Transfer(), m_eventType( eventType ), m_source( source ), m_timeStamp( timeStamp ) +{ + m_contentFlags |= ( EventType | Source | TimeStamp ); +} + + +EventTransfer::~EventTransfer() +{ +} + +// query contents + +bool EventTransfer::hasEventType() +{ + return ( m_contentFlags & EventType ); +} + +bool EventTransfer::hasSource() +{ + return ( m_contentFlags & Source ); +} + +bool EventTransfer::hasTimeStamp() +{ + return ( m_contentFlags & TimeStamp ); +} + +bool EventTransfer::hasGuid() +{ + return ( m_contentFlags & Guid ); +} + +bool EventTransfer::hasFlags() +{ + return ( m_contentFlags & Flags ); +} + +bool EventTransfer::hasMessage() +{ + return ( m_contentFlags & Message ); +} + +bool EventTransfer::hasStatus() +{ + return ( m_contentFlags & Status ); +} + +bool EventTransfer::hasStatusText() +{ + return ( m_contentFlags & StatusText ); +} + +// accessors + +int EventTransfer::eventType() +{ + return m_eventType; +} + +QString EventTransfer::source() +{ + return m_source; +} + +QDateTime EventTransfer::timeStamp() +{ + return m_timeStamp; +} + +GroupWise::ConferenceGuid EventTransfer::guid() +{ + return m_guid; +} + +Q_UINT32 EventTransfer::flags() +{ + return m_flags; +} + +QString EventTransfer::message() +{ + return m_message; +} + +Q_UINT16 EventTransfer::status() +{ + return m_status; +} + +QString EventTransfer::statusText() +{ + return m_statusText; +} + +// mutators +void EventTransfer::setGuid( const GroupWise::ConferenceGuid & guid ) +{ + m_contentFlags |= Guid; + m_guid = guid; +} + +void EventTransfer::setFlags( const Q_UINT32 flags ) +{ + m_contentFlags |= Flags; + m_flags = flags; +} + +void EventTransfer::setMessage( const QString & message ) +{ + m_contentFlags |= Message; + m_message = message; +} + +void EventTransfer::setStatus( const Q_UINT16 inStatus ) +{ + m_contentFlags |= Status; + m_status = inStatus; +} + +void EventTransfer::setStatusText( const QString & statusText ) +{ + m_contentFlags |= StatusText; + m_statusText = statusText; +} + diff --git a/kopete/protocols/groupwise/libgroupwise/eventtransfer.h b/kopete/protocols/groupwise/libgroupwise/eventtransfer.h new file mode 100644 index 00000000..335d4593 --- /dev/null +++ b/kopete/protocols/groupwise/libgroupwise/eventtransfer.h @@ -0,0 +1,110 @@ +/* + eventtransfer.h - Kopete Groupwise Protocol + + Copyright (c) 2004 SUSE Linux AG http://www.suse.com + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#ifndef GW_EVENTTRANSFER_H +#define GW_EVENTTRANSFER_H + +#include <qcstring.h> +#include <qdatetime.h> + +#include "gwerror.h" + +#include "transfer.h" + +namespace Event { + +} + +/** + * Transfer representing an event, a message generated by the server in response to external stimulus + * This class can contain varying data items depending on the type of event. + * You can query which data is present before trying to access it + * @author Kopete Developers + */ +class EventTransfer : public Transfer +{ +public: + /** + * Flags describing the possible contents of an event transfer + */ + enum Contents { EventType = 0x00000001, + Source = 0x00000002, + TimeStamp = 0x00000004, + Guid = 0x00000008, + Flags = 0x00000010, + Message = 0x00000020, + Status = 0x00000040, + StatusText = 0x00000080 }; + /** + * Constructor + * @param eventType the event code describing the event, see @refGroupWise::Event. + * @param source the user generating the event. + * @param timeStamp the time at which the event was received. + */ + EventTransfer( const Q_UINT32 eventType, const QString & source, QDateTime timeStamp ); + ~EventTransfer(); + /** + * Access the bitmask that describes the transfer's contents. Use @ref Contents to determine what it contains + */ + Q_UINT32 contents(); + /** + * Convenience accessors to see what the transfer contains + */ + bool hasEventType(); + bool hasSource(); + bool hasTimeStamp(); + bool hasGuid(); + bool hasFlags(); + bool hasMessage(); + bool hasStatus(); + bool hasStatusText(); + + /** + * Accessors for the transfer's contents + */ + TransferType type() { return Transfer::EventTransfer; } + int eventType(); + QString source(); + QDateTime timeStamp(); + GroupWise::ConferenceGuid guid(); + Q_UINT32 flags(); + QString message(); + Q_UINT16 status(); + QString statusText(); + + /** + * Mutators to set the transfer's contents + */ + void setGuid( const GroupWise::ConferenceGuid & guid ); + void setFlags( const Q_UINT32 flags ); + void setMessage( const QString & message ); + void setStatus( const Q_UINT16 status ); + void setStatusText( const QString & statusText); + +private: + Q_UINT32 m_contentFlags; + int m_eventType; + QString m_source; + QDateTime m_timeStamp; + GroupWise::ConferenceGuid m_guid; + Q_UINT32 m_flags; + QString m_message; + Q_UINT16 m_status; + QString m_statusText; +}; + +#endif diff --git a/kopete/protocols/groupwise/libgroupwise/gwchatrooms.h b/kopete/protocols/groupwise/libgroupwise/gwchatrooms.h new file mode 100644 index 00000000..207531bb --- /dev/null +++ b/kopete/protocols/groupwise/libgroupwise/gwchatrooms.h @@ -0,0 +1,78 @@ +/* + Kopete Groupwise Protocol + gwchatrooms.h - Data types for group chat + + Copyright (c) 2005 SUSE Linux AG http://www.suse.com + + Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#include <qdatetime.h> +#include <qvaluelist.h> + +#ifndef GROUPWISE_CHATROOMS_H +#define GROUPWISE_CHATROOMS_H + +namespace GroupWise +{ + +class ChatContact +{ + public: + QString dn; + uint chatRights; +}; +typedef QValueList<GroupWise::ChatContact> ChatContactList; + +struct ChatroomSearchResult +{ + QString name; + QString ownerDN; + uint participants; +}; + + +class Chatroom +{ + public: + enum UserStatus { Participating, NotParticipating }; + enum Rights { Read = 1, Write = 2, Modify = 4, Moderator = 8, Owner = 16 }; + QString creatorDN; + QString description; + QString disclaimer; + QString displayName; + QString objectId; + QString ownerDN; + QString query; + QString topic; + bool archive; + uint maxUsers; + uint chatRights; + UserStatus userStatus; + QDateTime createdOn; + uint participantsCount; + // haveParticipants, Acl, Invites indicate if we have obtained these lists from the server, so we can tell 'not fetched list' and 'fetched empty list' apart. + bool haveParticipants; + ChatContactList participants; + bool haveAcl; + ChatContactList acl; + bool haveInvites; + ChatContactList invites; + + Chatroom() { archive = false; maxUsers = 0; chatRights = 0; participantsCount = 0; haveParticipants = false; haveAcl = false; haveInvites = false; } + Chatroom( ChatroomSearchResult csr ) { archive = false; maxUsers = 0; chatRights = 0; participantsCount = csr.participants; haveParticipants = false; haveAcl = false; haveInvites = false; ownerDN = csr.ownerDN; displayName = csr.name; } +}; + +typedef QValueList<Chatroom> ChatroomList; +typedef QMap<QString, Chatroom> ChatroomMap; +} +#endif diff --git a/kopete/protocols/groupwise/libgroupwise/gwclientstream.cpp b/kopete/protocols/groupwise/libgroupwise/gwclientstream.cpp new file mode 100644 index 00000000..7d58de93 --- /dev/null +++ b/kopete/protocols/groupwise/libgroupwise/gwclientstream.cpp @@ -0,0 +1,606 @@ +/* + gwclientstream.cpp - Kopete Groupwise Protocol + + Copyright (c) 2004 SUSE Linux AG http://www.suse.com + + Based on Iris, Copyright (C) 2003 Justin Karneges + encode_method from Gaim src/protocols/novell/nmconn.c + Copyright (c) 2004 Novell, Inc. All Rights Reserved + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +//#include<qtextstream.h> +//#include<qguardedptr.h> +// #include<qca.h> +// #include<stdlib.h> +// #include"bytestream.h" +// #include"base64.h" +// #include"hash.h" +// #include"simplesasl.h" +// #include"securestream.h" +// #include"protocol.h" + +#include <qapplication.h> // for qdebug +#include <qguardedptr.h> +#include <qobject.h> +#include <qptrqueue.h> +#include <qtimer.h> + +#include "bytestream.h" +#include "connector.h" +#include "coreprotocol.h" +#include "request.h" +#include "securestream.h" +#include "tlshandler.h" + +//#include "iostream.h" + +#include "gwclientstream.h" + +//#define LIBGW_DEBUG 1 + +void cs_dump( const QByteArray &bytes ); + +enum { + Idle, + Connecting, + WaitVersion, + WaitTLS, + NeedParams, + Active, + Closing +}; + +enum { + Client, + Server +}; + +class ClientStream::Private +{ +public: + Private() + { + conn = 0; + bs = 0; + ss = 0; + tlsHandler = 0; + tls = 0; +// sasl = 0; + in.setAutoDelete(true); + + allowPlain = false; + mutualAuth = false; + haveLocalAddr = false; +/* minimumSSF = 0; + maximumSSF = 0;*/ + doBinding = true; + + in_rrsig = false; + + reset(); + } + void reset() + { + state = Idle; + notify = 0; + newTransfers = false; +// sasl_ssf = 0; + tls_warned = false; + using_tls = false; + } + + NovellDN id; + QString server; + bool oldOnly; + bool allowPlain, mutualAuth; + bool haveLocalAddr; + QHostAddress localAddr; + Q_UINT16 localPort; +// int minimumSSF, maximumSSF; +// QString sasl_mech; + bool doBinding; + + bool in_rrsig; + + Connector *conn; + ByteStream *bs; + TLSHandler *tlsHandler; + QCA::TLS *tls; +// QCA::SASL *sasl; + SecureStream *ss; + CoreProtocol client; + //CoreProtocol srv; + + QString defRealm; + + int mode; + int state; + int notify; + bool newTransfers; +// int sasl_ssf; + bool tls_warned, using_tls; + bool doAuth; + +// QStringList sasl_mechlist; + + int errCond; + QString errText; + + QPtrQueue<Transfer> in; + + QTimer noopTimer; // probably not needed + int noop_time; +}; + +ClientStream::ClientStream(Connector *conn, TLSHandler *tlsHandler, QObject *parent) +:Stream(parent) +{ + d = new Private; + d->mode = Client; + d->conn = conn; + connect( d->conn, SIGNAL(connected()), SLOT(cr_connected()) ); + connect( d->conn, SIGNAL(error()), SLOT(cr_error()) ); + connect( &d->client, SIGNAL( outgoingData( const QByteArray& ) ), SLOT ( cp_outgoingData( const QByteArray & ) ) ); + connect( &d->client, SIGNAL( incomingData() ), SLOT ( cp_incomingData() ) ); + + d->noop_time = 0; + connect(&d->noopTimer, SIGNAL(timeout()), SLOT(doNoop())); + + d->tlsHandler = tlsHandler; // all the extra stuff happening in the larger ctor happens at connect time :) +} + +ClientStream::~ClientStream() +{ + reset(); + delete d; +} + +void ClientStream::reset(bool all) +{ + d->reset(); + d->noopTimer.stop(); + + // delete securestream + delete d->ss; + d->ss = 0; + + // reset sasl +// delete d->sasl; +// d->sasl = 0; + + // client + if(d->mode == Client) { + // reset tls + if(d->tlsHandler) + d->tlsHandler->reset(); + + // reset connector + if(d->bs) { + d->bs->close(); + d->bs = 0; + } + d->conn->done(); + + // reset state machine + d->client.reset(); + } + if(all) + d->in.clear(); +} + +// Jid ClientStream::jid() const +// { +// return d->jid; +// } + +void ClientStream::connectToServer(const NovellDN &id, bool auth) +{ + reset(true); + d->state = Connecting; + d->id = id; + d->doAuth = auth; + d->server = d->id.server; + + d->conn->connectToServer( d->server ); +} + +void ClientStream::continueAfterWarning() +{ + if(d->state == WaitVersion) { + // if we don't have TLS yet, then we're never going to get it + if(!d->tls_warned && !d->using_tls) { + d->tls_warned = true; + d->state = WaitTLS; + emit warning(WarnNoTLS); + return; + } + d->state = Connecting; + processNext(); + } + else if(d->state == WaitTLS) { + d->state = Connecting; + processNext(); + } +} + +void ClientStream::accept() +{ +/* d->srv.host = d->server; + processNext();*/ +} + +bool ClientStream::isActive() const +{ + return (d->state != Idle); +} + +bool ClientStream::isAuthenticated() const +{ + return (d->state == Active); +} + +// void ClientStream::setPassword(const QString &s) +// { +// if(d->client.old) { +// d->client.setPassword(s); +// } +// else { +// if(d->sasl) +// d->sasl->setPassword(s); +// } +// } + +// void ClientStream::setRealm(const QString &s) +// { +// if(d->sasl) +// d->sasl->setRealm(s); +// } + +void ClientStream::continueAfterParams() +{ +/* if(d->state == NeedParams) { + d->state = Connecting; + if(d->client.old) { + processNext(); + } + else { + if(d->sasl) + d->sasl->continueAfterParams(); + } + }*/ +} + +void ClientStream::setNoopTime(int mills) +{ + d->noop_time = mills; + + if(d->state != Active) + return; + + if(d->noop_time == 0) { + d->noopTimer.stop(); + return; + } + d->noopTimer.start(d->noop_time); +} + +void ClientStream::setLocalAddr(const QHostAddress &addr, Q_UINT16 port) +{ + d->haveLocalAddr = true; + d->localAddr = addr; + d->localPort = port; +} + +int ClientStream::errorCondition() const +{ + return d->errCond; +} + +QString ClientStream::errorText() const +{ + return d->errText; +} + +// QDomElement ClientStream::errorAppSpec() const +// { +// return d->errAppSpec;cr_error +// } + +// bool ClientStream::old() const +// { +// return d->client.old; +// } + +void ClientStream::close() +{ + if(d->state == Active) { + d->state = Closing; +// d->client.shutdown(); + processNext(); + } + else if(d->state != Idle && d->state != Closing) { + reset(); + } +} + +void ClientStream::setAllowPlain(bool b) +{ + d->allowPlain = b; +} + +void ClientStream::setRequireMutualAuth(bool b) +{ + d->mutualAuth = b; +} + +// void ClientStream::setSSFRange(int low, int high) +// { +// d->minimumSSF = low; +// d->maximumSSF = high; +// } + +// void ClientStream::setOldOnly(bool b) +// { +// d->oldOnly = b; +// } + +bool ClientStream::transfersAvailable() const +{ + return ( !d->in.isEmpty() ); +} + +Transfer * ClientStream::read() +{ + if(d->in.isEmpty()) + return 0; //first from queue... + else + return d->in.dequeue(); +} + +void ClientStream::write( Request *request ) +{ + // pass to CoreProtocol for transformation into wire format + d->client.outgoingTransfer( request ); +} + +void cs_dump( const QByteArray &bytes ) +{ +//#define GW_CLIENTSTREAM_DEBUG 1 +#ifdef GW_CLIENTSTREAM_DEBUG + CoreProtocol::debug( QString( "contains: %1 bytes " ).arg( bytes.count() ) ); + uint count = 0; + while ( count < bytes.count() ) + { + int dword = 0; + for ( int i = 0; i < 8; ++i ) + { + if ( count + i < bytes.count() ) + printf( "%02x ", bytes[ count + i ] ); + else + printf( " " ); + if ( i == 3 ) + printf( " " ); + } + printf(" | "); + dword = 0; + for ( int i = 0; i < 8; ++i ) + { + if ( count + i < bytes.count() ) + { + int j = bytes [ count + i ]; + if ( j >= 0x20 && j <= 0x7e ) + printf( "%2c ", j ); + else + printf( "%2c ", '.' ); + } + else + printf( " " ); + if ( i == 3 ) + printf( " " ); + } + printf( "\n" ); + count += 8; + } + printf( "\n" ); +#else + Q_UNUSED( bytes ); +#endif +} + +void ClientStream::cp_outgoingData( const QByteArray& outgoingBytes ) +{ + // take formatted bytes from CoreProtocol and put them on the wire +#ifdef LIBGW_DEBUG + CoreProtocol::debug( "ClientStream::cp_outgoingData:" ); + cs_dump( outgoingBytes ); +#endif + d->ss->write( outgoingBytes ); +} + +void ClientStream::cp_incomingData() +{ + CoreProtocol::debug( "ClientStream::cp_incomingData:" ); + Transfer * incoming = d->client.incomingTransfer(); + if ( incoming ) + { + CoreProtocol::debug( " - got a new transfer" ); + d->in.enqueue( incoming ); + d->newTransfers = true; + emit doReadyRead(); + } + else + CoreProtocol::debug( QString( " - client signalled incomingData but none was available, state is: %1" ).arg( d->client.state() ) ); +} + +void ClientStream::cr_connected() +{ + d->bs = d->conn->stream(); + connect(d->bs, SIGNAL(connectionClosed()), SLOT(bs_connectionClosed())); + connect(d->bs, SIGNAL(delayedCloseFinished()), SLOT(bs_delayedCloseFinished())); + + QByteArray spare = d->bs->read(); + + d->ss = new SecureStream(d->bs); + connect(d->ss, SIGNAL(readyRead()), SLOT(ss_readyRead())); + connect(d->ss, SIGNAL(bytesWritten(int)), SLOT(ss_bytesWritten(int))); + connect(d->ss, SIGNAL(tlsHandshaken()), SLOT(ss_tlsHandshaken())); + connect(d->ss, SIGNAL(tlsClosed()), SLOT(ss_tlsClosed())); + connect(d->ss, SIGNAL(error(int)), SLOT(ss_error(int))); + + //d->client.startDialbackOut("andbit.net", "im.pyxa.org"); + //d->client.startServerOut(d->server); + +// d->client.startClientOut(d->jid, d->oldOnly, d->conn->useSSL(), d->doAuth); +// d->client.setAllowTLS(d->tlsHandler ? true: false); +// d->client.setAllowBind(d->doBinding); +// d->client.setAllowPlain(d->allowPlain); + + /*d->client.jid = d->jid; + d->client.server = d->server; + d->client.allowPlain = d->allowPlain; + d->client.oldOnly = d->oldOnly; + d->client.sasl_mech = d->sasl_mech; + d->client.doTLS = d->tlsHandler ? true: false; + d->client.doBinding = d->doBinding;*/ + + QGuardedPtr<QObject> self = this; + emit connected(); + if(!self) + return; + + // immediate SSL? + if(d->conn->useSSL()) { + CoreProtocol::debug( "CLIENTSTREAM: cr_connected(), starting TLS" ); + d->using_tls = true; + d->ss->startTLSClient(d->tlsHandler, d->server, spare); + } + else { +/* d->client.addIncomingData(spare); + processNext();*/ + } +} + +void ClientStream::cr_error() +{ + reset(); + emit error(ErrConnection); +} + +void ClientStream::bs_connectionClosed() +{ + reset(); + emit connectionClosed(); +} + +void ClientStream::bs_delayedCloseFinished() +{ + // we don't care about this (we track all important data ourself) +} + +void ClientStream::bs_error(int) +{ + // TODO +} + +void ClientStream::ss_readyRead() +{ + QByteArray a; + a = d->ss->read(); + +#ifdef LIBGW_DEBUG + QCString cs(a.data(), a.size()+1); + CoreProtocol::debug( QString( "ClientStream: ss_readyRead() recv: %1 bytes" ).arg( a.size() ) ); + cs_dump( a ); +#endif + + d->client.addIncomingData(a); +/* if(d->notify & CoreProtocol::NRecv) { */ + //processNext(); +} + +void ClientStream::ss_bytesWritten(int bytes) +{ +#ifdef LIBGW_DEBUG + CoreProtocol::debug( QString( "ClientStream::ss_bytesWritten: %1 bytes written" ).arg( bytes ) ); +#else + Q_UNUSED( bytes ); +#endif +} + +void ClientStream::ss_tlsHandshaken() +{ + QGuardedPtr<QObject> self = this; + emit securityLayerActivated(LayerTLS); + if(!self) + return; + processNext(); +} + +void ClientStream::ss_tlsClosed() +{ + CoreProtocol::debug( "ClientStream::ss_tlsClosed()" ); + reset(); + emit connectionClosed(); +} + +void ClientStream::ss_error(int x) +{ + CoreProtocol::debug( QString( "ClientStream::ss_error() x=%1 ").arg( x ) ); + if(x == SecureStream::ErrTLS) { + reset(); + d->errCond = TLSFail; + emit error(ErrTLS); + } + else { + reset(); + emit error(ErrSecurityLayer); + } +} + +void ClientStream::srvProcessNext() +{ +} + +void ClientStream::doReadyRead() +{ + //QGuardedPtr<QObject> self = this; + emit readyRead(); + //if(!self) + // return; + //d->in_rrsig = false; +} + +void ClientStream::processNext() +{ + if( !d->in.isEmpty() ) { + //d->in_rrsig = true; + QTimer::singleShot(0, this, SLOT(doReadyRead())); + } +} + +bool ClientStream::handleNeed() +{ + return false; +} + + +void ClientStream::doNoop() +{ +} + +void ClientStream::handleError() +{ +} + +#include "gwclientstream.moc" diff --git a/kopete/protocols/groupwise/libgroupwise/gwclientstream.h b/kopete/protocols/groupwise/libgroupwise/gwclientstream.h new file mode 100644 index 00000000..5ae5ec8c --- /dev/null +++ b/kopete/protocols/groupwise/libgroupwise/gwclientstream.h @@ -0,0 +1,185 @@ +/* + gwclientstream.h - Kopete Groupwise Protocol + + Copyright (c) 2004 SUSE Linux AG http://www.suse.com + + Based on Iris, Copyright (C) 2003 Justin Karneges + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#ifndef GW_CLIENTSTREAM_H +#define GW_CLIENTSTREAM_H + +#include <qca.h> + +#include "gwfield.h" +#include "stream.h" + +// forward defines +class ByteStream; +class Connector; +class Request; +class TLSHandler; + +typedef struct NovellDN +{ + QString dn; + QString server; +}; + +class ClientStream : public Stream +{ + Q_OBJECT +public: + enum Error { + ErrConnection = ErrCustom, // Connection error, ask Connector-subclass what's up + ErrNeg, // Negotiation error, see condition + ErrTLS, // TLS error, see condition + ErrAuth, // Auth error, see condition + ErrSecurityLayer, // broken SASL security layer + ErrBind // Resource binding error + }; + enum Warning { +/*# WarnOldVersion, // server uses older XMPP/Jabber "0.9" protocol // can be customised for novell versions*/ + WarnNoTLS // there is no chance for TLS at this point + }; + enum NegCond { + HostGone, // host no longer hosted + HostUnknown, // unknown host + RemoteConnectionFailed, // unable to connect to a required remote resource + SeeOtherHost, // a 'redirect', see errorText() for other host + UnsupportedVersion // unsupported XMPP version + }; + enum TLSCond { + TLSStart, // server rejected STARTTLS + TLSFail // TLS failed, ask TLSHandler-subclass what's up + }; + enum SecurityLayer { + LayerTLS, + LayerSASL + }; + enum AuthCond { + GenericAuthError, // all-purpose "can't login" error + NoMech, // No appropriate auth mech available + BadProto, // Bad SASL auth protocol + BadServ, // Server failed mutual auth + EncryptionRequired, // can't use mech without TLS +/*# InvalidAuthzid, // bad input JID // need to change this to novell DN*/ + InvalidMech, // bad mechanism + InvalidRealm, // bad realm + MechTooWeak, // can't use mech with this authzid + NotAuthorized, // bad user, bad password, bad creditials + TemporaryAuthFailure // please try again later! + }; + enum BindCond { + BindNotAllowed, // not allowed to bind a resource + BindConflict // resource in-use + }; + + ClientStream(Connector *conn, TLSHandler *tlsHandler=0, QObject *parent=0); + ~ClientStream(); + + void connectToServer(const NovellDN &id, bool auth=true); + void accept(); // server + bool isActive() const; + bool isAuthenticated() const; + + // login params + void setUsername(const QString &s); + void setPassword(const QString &s); + void setRealm(const QString &s); + void continueAfterParams(); + + // security options (old protocol only uses the first !) + void setAllowPlain(bool); + void setRequireMutualAuth(bool); + void setLocalAddr(const QHostAddress &addr, Q_UINT16 port); + + void close(); + + /** + * Are there any messages waiting to be read + */ + bool transfersAvailable() const; + /** + * Read a message received from the server + */ + Transfer * read(); + + /** + * Send a message to the server + */ + void write( Request * request ); + + int errorCondition() const; + QString errorText() const; +// # QDomElement errorAppSpec() const; // redondo + + // extrahttp://bugs.kde.org/show_bug.cgi?id=85158 +/*# void writeDirect(const QString &s); // must be for debug testing*/ + void setNoopTime(int mills); + +signals: + void connected(); + void securityLayerActivated(int); + //void needAuthParams(bool user, bool pass, bool realm); + void authenticated(); // this signal is ordinarily emitted in processNext + void warning(int); +// # void incomingXml(const QString &s); // signals emitted in processNext but don't seem to go anywhere... +// # void outgoingXml(const QString &s); // +// void readyRead(); //signals that there is a transfer ready to be read - defined in stream +public slots: + void continueAfterWarning(); + +private slots: + void cr_connected(); + void cr_error(); + /** + * collects wire ready outgoing data from the core protocol and sends + */ + void cp_outgoingData( const QByteArray& ); + /** + * collects parsed incoming data as a transfer from the core protocol and queues + */ + void cp_incomingData(); + + void bs_connectionClosed(); + void bs_delayedCloseFinished(); + void bs_error(int); // server only + + void ss_readyRead(); + void ss_bytesWritten(int); + void ss_tlsHandshaken(); + void ss_tlsClosed(); + void ss_error(int); + + void doNoop(); + void doReadyRead(); + +private: + class Private; + Private *d; + + void reset(bool all=false); + void processNext(); + bool handleNeed(); + void handleError(); + void srvProcessNext(); + + /** + * convert internal method representation to wire + */ + static char* encode_method(Q_UINT8 method); +}; + +#endif diff --git a/kopete/protocols/groupwise/libgroupwise/gwerror.cpp b/kopete/protocols/groupwise/libgroupwise/gwerror.cpp new file mode 100644 index 00000000..5338cea0 --- /dev/null +++ b/kopete/protocols/groupwise/libgroupwise/gwerror.cpp @@ -0,0 +1,276 @@ +/* + gwerror.cpp - Kopete Groupwise Protocol + + Copyright (c) 2007 Novell, Inc http://www.novell.com/linux + + Kopete (c) 2002-2007 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#include <klocale.h> + +#include "gwerror.h" + +QString GroupWise::errorCodeToString( int errorCode ) +{ + QString errorString; + switch ( errorCode ) + { +#if 0 + case NMERR_ACCESS_DENIED: + errorString = i18n( "Access denied" ); + break; + case NMERR_NOT_SUPPORTED: + errorString = i18n( "Not supported" ); + break; + case NMERR_PASSWORD_EXPIRED: + errorString = i18n( "Password expired" ); + break; + case NMERR_PASSWORD_INVALID: + errorString = i18n( "Invalid password" ); + break; + case NMERR_USER_NOT_FOUND: + errorString = i18n( "User not found" ); + break; + case NMERR_ATTRIBUTE_NOT_FOUND: + errorString = i18n( "Attribute not found" ); + break; + case NMERR_USER_DISABLED: + errorString = i18n( "User is disabled" ); + break; + case NMERR_DIRECTORY_FAILURE: + errorString = i18n( "Directory failure" ); + break; + case NMERR_HOST_NOT_FOUND: + errorString = i18n( "Host not found" ); + break; + case NMERR_ADMIN_LOCKED: + errorString = i18n( "Locked by admin" ); + break; + case NMERR_DUPLICATE_PARTICIPANT: + errorString = i18n( "Duplicate participant" ); + break; + case NMERR_SERVER_BUSY: + errorString = i18n( "Server busy" ); + break; + case NMERR_OBJECT_NOT_FOUND: + errorString = i18n( "Object not found" ); + break; + case NMERR_DIRECTORY_UPDATE: + errorString = i18n( "Directory update" ); + break; + case NMERR_DUPLICATE_FOLDER: + errorString = i18n( "Duplicate folder" ); + break; + case NMERR_DUPLICATE_CONTACT: + errorString = i18n( "Contact list entry already exists" ); + break; + case NMERR_USER_NOT_ALLOWED: + errorString = i18n( "User not allowed" ); + break; + case NMERR_TOO_MANY_CONTACTS: + errorString = i18n( "Too many contacts" ); + break; + case NMERR_CONFERENCE_NOT_FOUND_2: + errorString = i18n( "Conference not found" ); + break; + case NMERR_TOO_MANY_FOLDERS: + errorString = i18n( "Too many folders" ); + break; + case NMERR_SERVER_PROTOCOL: + errorString = i18n( "Server protocol error" ); + break; + case NMERR_CONVERSATION_INVITE: + errorString = i18n( "Conversation invitation error" ); + break; + case NMERR_USER_BLOCKED: + errorString = i18n( "User is blocked" ); + break; + case NMERR_MASTER_ARCHIVE_MISSING: + errorString = i18n( "Master archive is missing" ); + break; + case NMERR_PASSWORD_EXPIRED_2: + errorString = i18n( "Expired password in use" ); + break; + case NMERR_CREDENTIALS_MISSING: + errorString = i18n( "Credentials missing" ); + break; + case NMERR_AUTHENTICATION_FAILED: + errorString = i18n( "Authentication failed" ); + break; + case NMERR_EVAL_CONNECTION_LIMIT: + errorString = i18n( "Eval connection limit" ); + break; + case MSGPRES_ERR_UNSUPPORTED_CLIENT_VERSION: + errorString = i18n( "Unsupported client version" ); + break; + case MSGPRES_ERR_DUPLICATE_CHAT: + errorString = i18n( "A duplicate chat was found" ); + break; + case MSGPRES_ERR_CHAT_NOT_FOUND: + errorString = i18n( "Chat not found" ); + break; + case MSGPRES_ERR_INVALID_NAME: + errorString = i18n( "Invalid chat name" ); + break; + case MSGPRES_ERR_CHAT_ACTIVE: + errorString = i18n( "The chat is active" ); + break; + case MSGPRES_ERR_CHAT_BUSY: + errorString = i18n( "Chat is busy; try again" ); + break; + case MSGPRES_ERR_REQUEST_TOO_SOON: + errorString = i18n( "Tried request too soon after another; try again" ); + break; + case MSGPRES_ERR_CHAT_NOT_ACTIVE: + errorString = i18n( "Server's chat subsystem is not active" ); + break; + case MSGPRES_ERR_INVALID_CHAT_UPDATE: + errorString = i18n( "The chat update request is invalid" ); + break; + case MSGPRES_ERR_DIRECTORY_MISMATCH: + errorString = i18n( "Write failed due to directory mismatch" ); + break; + case MSGPRES_ERR_RECIPIENT_TOO_OLD: + errorString = i18n( "Recipient's client version is too old" ); + break; + case MSGPRES_ERR_CHAT_NO_LONGER_VALID: + errorString = i18n( "Chat has been removed from server" ); + break; + default: + errorString = i18n("Unrecognized error code: %s").arg( errorCode ); +#else + case NMERR_ACCESS_DENIED: + errorString = "Access denied"; + break; + case NMERR_NOT_SUPPORTED: + errorString = "Not supported"; + break; + case NMERR_PASSWORD_EXPIRED: + errorString = "Password expired"; + break; + case NMERR_PASSWORD_INVALID: + errorString = "Invalid password"; + break; + case NMERR_USER_NOT_FOUND: + errorString = "User not found"; + break; + case NMERR_ATTRIBUTE_NOT_FOUND: + errorString = "Attribute not found"; + break; + case NMERR_USER_DISABLED: + errorString = "User is disabled"; + break; + case NMERR_DIRECTORY_FAILURE: + errorString = "Directory failure"; + break; + case NMERR_HOST_NOT_FOUND: + errorString = "Host not found"; + break; + case NMERR_ADMIN_LOCKED: + errorString = "Locked by admin"; + break; + case NMERR_DUPLICATE_PARTICIPANT: + errorString = "Duplicate participant"; + break; + case NMERR_SERVER_BUSY: + errorString = "Server busy"; + break; + case NMERR_OBJECT_NOT_FOUND: + errorString = "Object not found"; + break; + case NMERR_DIRECTORY_UPDATE: + errorString = "Directory update"; + break; + case NMERR_DUPLICATE_FOLDER: + errorString = "Duplicate folder"; + break; + case NMERR_DUPLICATE_CONTACT: + errorString = "Contact list entry already exists"; + break; + case NMERR_USER_NOT_ALLOWED: + errorString = "User not allowed"; + break; + case NMERR_TOO_MANY_CONTACTS: + errorString = "Too many contacts"; + break; + case NMERR_CONFERENCE_NOT_FOUND_2: + errorString = "Conference not found"; + break; + case NMERR_TOO_MANY_FOLDERS: + errorString = "Too many folders"; + break; + case NMERR_SERVER_PROTOCOL: + errorString = "Server protocol error"; + break; + case NMERR_CONVERSATION_INVITE: + errorString = "Conversation invitation error"; + break; + case NMERR_USER_BLOCKED: + errorString = "User is blocked"; + break; + case NMERR_MASTER_ARCHIVE_MISSING: + errorString = "Master archive is missing"; + break; + case NMERR_PASSWORD_EXPIRED_2: + errorString = "Expired password in use"; + break; + case NMERR_CREDENTIALS_MISSING: + errorString = "Credentials missing"; + break; + case NMERR_AUTHENTICATION_FAILED: + errorString = "Authentication failed"; + break; + case NMERR_EVAL_CONNECTION_LIMIT: + errorString = "Eval connection limit"; + break; + case MSGPRES_ERR_UNSUPPORTED_CLIENT_VERSION: + errorString = "Unsupported client version"; + break; + case MSGPRES_ERR_DUPLICATE_CHAT: + errorString = "A duplicate chat was found"; + break; + case MSGPRES_ERR_CHAT_NOT_FOUND: + errorString = "Chat not found"; + break; + case MSGPRES_ERR_INVALID_NAME: + errorString = "Invalid chat name"; + break; + case MSGPRES_ERR_CHAT_ACTIVE: + errorString = "The chat is active"; + break; + case MSGPRES_ERR_CHAT_BUSY: + errorString = "Chat is busy; try again"; + break; + case MSGPRES_ERR_REQUEST_TOO_SOON: + errorString = "Tried request too soon after another; try again"; + break; + case MSGPRES_ERR_CHAT_NOT_ACTIVE: + errorString = "Server's chat subsystem is not active"; + break; + case MSGPRES_ERR_INVALID_CHAT_UPDATE: + errorString = "The chat update request is invalid"; + break; + case MSGPRES_ERR_DIRECTORY_MISMATCH: + errorString = "Write failed due to directory mismatch"; + break; + case MSGPRES_ERR_RECIPIENT_TOO_OLD: + errorString = "Recipient's client version is too old"; + break; + case MSGPRES_ERR_CHAT_NO_LONGER_VALID: + errorString = "Chat has been removed from server"; + break; + default: + errorString = QString("Unrecognized error code: %s").arg( errorCode ); +#endif + } + return errorString; +}
\ No newline at end of file diff --git a/kopete/protocols/groupwise/libgroupwise/gwerror.h b/kopete/protocols/groupwise/libgroupwise/gwerror.h new file mode 100644 index 00000000..5300f788 --- /dev/null +++ b/kopete/protocols/groupwise/libgroupwise/gwerror.h @@ -0,0 +1,241 @@ +/* + gwerror.h - Kopete Groupwise Protocol + + Copyright (c) 2004-2007 Novell, Inc http://www.novell.com/linux + + Kopete (c) 2002-2007 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#ifndef GW_ERROR_H +#define GW_ERROR_H + +#include <qdatetime.h> +#include <qglobal.h> +#include <qmap.h> +#include <qstring.h> + +typedef Q_UINT16 NMERR_T; +#define GROUPWISE_DEBUG_GLOBAL 14190 +#define GROUPWISE_DEBUG_LIBGW 14191 +#define GROUPWISE_DEBUG_RAW 14192 + +#define BLANK_GUID "[00000000-00000000-00000000-0000-0000]" +#define CONF_GUID_END 27 + +//#define LIBGW_DEBUG 1 +#define LIBGW_USE_KDEBUG 1 + +namespace GroupWise +{ + enum Status { Unknown = 0, + Offline = 1, + Available = 2, + Busy = 3, + Away = 4, + AwayIdle = 5, + Invalid = 6 + }; + + enum Error { None = 0, + ErrorBase = 0x2000L, + BadParm, + TCPWrite, + TCPRead, + Protocol, + ServerRedirect, + ConferenceNotFound, + ConferenceNotInstantiated, + FolderExists + }; + + enum Event { InvalidRecipient = 101, + UndeliverableStatus = 102, + StatusChange = 103, + ContactAdd = 104, + ConferenceClosed = 105, + ConferenceJoined = 106, + ConferenceLeft = 107, + ReceiveMessage = 108, + ReceiveFile = 109, + UserTyping = 112, + UserNotTyping = 113, + UserDisconnect = 114, + ServerDisconnect = 115, + ConferenceRename = 116, + ConferenceInvite = 117, + ConferenceInviteNotify = 118, + ConferenceReject = 119, + ReceiveAutoReply = 121, + Start = InvalidRecipient, + /* Event codes >= 122 are new in GW7 protocol */ + ReceivedBroadcast = 122, + ReceivedSystemBroadcast = 123, + ConferenceAttribUpdate = 128, + ConferenceTopicChanged = 129, + ChatroomNameChanged = 130, + ConferenceRightsChanged = 131, + ConferenceRemoved = 132, /* you were kicked */ + ChatOwnerChanged = 133, + Stop = ChatOwnerChanged + + }; + + enum ConferenceFlags { Logging = 0x00000001, + Secure = 0x00000002, + Closed = 0x10000000 + }; + + QString errorCodeToString( int errorCode ); + + // helpful structs used to pass data between the client library and the application using it + class ConferenceGuid : public QString + { + public: + ConferenceGuid(); + ConferenceGuid( const QString & string ); + ~ConferenceGuid(); + }; + + bool operator==( const ConferenceGuid & g1, const ConferenceGuid & g2 ); + bool operator==( const QString & s, const ConferenceGuid & g ); + bool operator==( const ConferenceGuid & g, const QString & s ); + + struct ConferenceEvent + { + Event type; + ConferenceGuid guid; + QString user; + QDateTime timeStamp; + Q_UINT32 flags; + QString message; + }; + + struct FolderItem + { + uint id; + uint sequence; + uint parentId; + QString name; + }; + + struct ContactItem + { + uint id; + uint parentId; + uint sequence; + QString dn; + QString displayName; + }; + + struct ContactDetails + { + QString cn, + dn, + givenName, + surname, + fullName, + awayMessage, + authAttribute; + int status; + bool archive; + QMap< QString, QString > properties; + }; + + struct OutgoingMessage + { + ConferenceGuid guid; + QString message; + QString rtfMessage; + }; + + struct UserSearchQueryTerm + { + QString field; + QString argument; + int operation; + }; + + struct CustomStatus + { + GroupWise::Status status; + QString name; + QString autoReply; + }; +} + +// temporary typedef pending implementation + +// #define NMERR_BASE 0x2000L +// #define NM_OK 0L +// #define NMERR_BAD_PARM (NMERR_BASE + 0x0001) +// #define NMERR_TCP_WRITE (NMERR_BASE + 0x0002) +// #define NMERR_TCP_READ (NMERR_BASE + 0x0003) +// #define NMERR_PROTOCOL (NMERR_BASE + 0x0004) +// #define NMERR_SERVER_REDIRECT (NMERR_BASE + 0x0005) +// #define NMERR_CONFERENCE_NOT_FOUND (NMERR_BASE + 0x0006) +// #define NMERR_CONFERENCE_NOT_INSTANTIATED (NMERR_BASE + 0x0007) +// #define NMERR_FOLDER_EXISTS (NMERR_BASE + 0x0008) + +/* Errors that are returned from the server */ +#define NMERR_SERVER_BASE 0xD100L +#define NMERR_ACCESS_DENIED (NMERR_SERVER_BASE + 0x0006) +#define NMERR_NOT_SUPPORTED (NMERR_SERVER_BASE + 0x000A) +#define NMERR_PASSWORD_EXPIRED (NMERR_SERVER_BASE + 0x000B) +#define NMERR_PASSWORD_INVALID (NMERR_SERVER_BASE + 0x000C) +#define NMERR_USER_NOT_FOUND (NMERR_SERVER_BASE + 0x000D) +#define NMERR_ATTRIBUTE_NOT_FOUND (NMERR_SERVER_BASE + 0x000E) +#define NMERR_USER_DISABLED (NMERR_SERVER_BASE + 0x0010) +#define NMERR_DIRECTORY_FAILURE (NMERR_SERVER_BASE + 0x0011) +#define NMERR_HOST_NOT_FOUND (NMERR_SERVER_BASE + 0x0019) +#define NMERR_ADMIN_LOCKED (NMERR_SERVER_BASE + 0x001C) +#define NMERR_DUPLICATE_PARTICIPANT (NMERR_SERVER_BASE + 0x001F) +#define NMERR_SERVER_BUSY (NMERR_SERVER_BASE + 0x0023) +#define NMERR_OBJECT_NOT_FOUND (NMERR_SERVER_BASE + 0x0024) +#define NMERR_DIRECTORY_UPDATE (NMERR_SERVER_BASE + 0x0025) +#define NMERR_DUPLICATE_FOLDER (NMERR_SERVER_BASE + 0x0026) +#define NMERR_DUPLICATE_CONTACT (NMERR_SERVER_BASE + 0x0027) +#define NMERR_USER_NOT_ALLOWED (NMERR_SERVER_BASE + 0x0028) +#define NMERR_TOO_MANY_CONTACTS (NMERR_SERVER_BASE + 0x0029) +#define NMERR_CONFERENCE_NOT_FOUND_2 (NMERR_SERVER_BASE + 0x002B) +#define NMERR_TOO_MANY_FOLDERS (NMERR_SERVER_BASE + 0x002C) +#define NMERR_SERVER_PROTOCOL (NMERR_SERVER_BASE + 0x0030) +#define NMERR_CONVERSATION_INVITE (NMERR_SERVER_BASE + 0x0035) +#define NMERR_USER_BLOCKED (NMERR_SERVER_BASE + 0x0039) +#define NMERR_MASTER_ARCHIVE_MISSING (NMERR_SERVER_BASE + 0x003A) +#define NMERR_PASSWORD_EXPIRED_2 (NMERR_SERVER_BASE + 0x0042) +#define NMERR_CREDENTIALS_MISSING (NMERR_SERVER_BASE + 0x0046) +#define NMERR_AUTHENTICATION_FAILED (NMERR_SERVER_BASE + 0x0049) +#define NMERR_EVAL_CONNECTION_LIMIT (NMERR_SERVER_BASE + 0x004A) + +/* Error codes that are new in GW7 */ +#define MSGPRES_ERR_UNSUPPORTED_CLIENT_VERSION (NMERR_SERVER_BASE + 0x004B) // This version of the client is not supported. +#define MSGPRES_ERR_DUPLICATE_CHAT (NMERR_SERVER_BASE + 0x0051) // A duplicate chat was found. +#define MSGPRES_ERR_CHAT_NOT_FOUND (NMERR_SERVER_BASE + 0x0052) // The chat was not found. +#define MSGPRES_ERR_INVALID_NAME (NMERR_SERVER_BASE + 0x0053) // The chat name is not valid. +#define MSGPRES_ERR_CHAT_ACTIVE (NMERR_SERVER_BASE + 0x0054) // Cannot delete an active chat. +#define MSGPRES_ERR_INSUF_CONV_RIGHTS (NMERR_SERVER_BASE + 0x0055) // Insufficient conversation rights to perform an action. +#define MSGPRES_ERR_CHAT_BUSY (NMERR_SERVER_BASE + 0x0056) // Chat is busy; try again. +#define MSGPRES_ERR_REQUEST_TOO_SOON (NMERR_SERVER_BASE + 0x0057) // Tried a request too soon after another one; try again. +#define MSGPRES_INFO_NO_LIST_CHANGE (NMERR_SERVER_BASE + 0x0058) // The chat list has not changed since the last search. +#define MSGPRES_ERR_CHAT_NOT_ACTIVE (NMERR_SERVER_BASE + 0x0059) // The chat subsystem is not active! +#define MSGPRES_ERR_INVALID_CHAT_UPDATE (NMERR_SERVER_BASE + 0x005A) // The chat update request is invalid. +#define MSGPRES_ERR_DIRECTORY_MISMATCH (NMERR_SERVER_BASE + 0x005B) // Write failed due to directory mismatch. +#define MSGPRES_ERR_RECIPIENT_TOO_OLD (NMERR_SERVER_BASE + 0x005C) // The recipient's client version is too old. +#define MSGPRES_ERR_CHAT_NO_LONGER_VALID (NMERR_SERVER_BASE + 0x005D) // The chat has been removed from the server. + +/* protocol version capabilities */ +#define CMSGPRES_GW_6_5 2 +#define CMSGPRES_SUPPORTS_NO_DETAILS_ON_LOGIN 3 +#define CMSGPRES_SUPPORTS_BROADCAST 4 +#define CMSGPRES_SUPPORTS_CHAT 5 + +#endif diff --git a/kopete/protocols/groupwise/libgroupwise/gwfield.cpp b/kopete/protocols/groupwise/libgroupwise/gwfield.cpp new file mode 100644 index 00000000..e0d3c5db --- /dev/null +++ b/kopete/protocols/groupwise/libgroupwise/gwfield.cpp @@ -0,0 +1,223 @@ +/* + gwfield.cpp - Fields used for Request/Response data in GroupWise + Copyright (c) 2004 SUSE Linux AG http://www.suse.com + + Based on Testbed + Copyright (c) 2003 by Will Stephenson <will@stevello.free-online.co.uk> + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#include <qcstring.h> + +#include "gwerror.h" + +#ifdef LIBGW_USE_KDEBUG + #include <kdebug.h> +#endif + +#include "gwfield.h" +#include <iostream> + +using namespace Field; +using namespace std; + +/* === FieldList ==================================================== */ +FieldList::~FieldList() +{ +} + +FieldListIterator FieldList::find( QCString tag ) +{ + FieldListIterator it = begin(); + return find( it, tag ); +} + +FieldListIterator FieldList::find( FieldListIterator &it, QCString tag ) +{ + FieldListIterator theEnd = end(); + //cout << "FieldList::find() looking for " << tag.data() << endl; + for ( ; it != theEnd; ++it ) + { + //cout << " - on " << (*it)->tag().data() << endl; + if ( (*it)->tag() == tag ) + break; + } + return it; +} + +int FieldList::findIndex( QCString tag ) +{ + FieldListIterator it = begin(); + FieldListIterator theEnd = end(); + int index = 0; + for ( ; it != theEnd; ++it, ++index ) + if ( (*it)->tag() == tag ) + return index; + + return -1; +} + +void FieldList::dump( bool recursive, int offset ) +{ + const FieldListIterator myEnd = end(); + if ( !offset ) + kdDebug( GROUPWISE_DEBUG_LIBGW ) << k_funcinfo << ( recursive ? ", recursively" : ", non-recursive" ) << endl; + for( FieldListIterator it = begin(); it != myEnd; ++it ) + { + QString s; + s.fill(' ', offset*2 ); + s.append( (*it)->tag() ); + SingleField * sf; + if ( ( sf = dynamic_cast<SingleField*>( *it ) ) ) + { + s.append( " :" ); + s.append( sf->value().toString() ); + } + kdDebug( GROUPWISE_DEBUG_LIBGW ) << s << endl; + if ( recursive ) + { + MultiField * mf; + if ( ( mf = dynamic_cast<MultiField*>( *it ) ) ) + mf->fields().dump( recursive, offset+1 ); + } + } +} + +void FieldList::purge() +{ + Field::FieldListIterator it = begin(); + Field::FieldListIterator theEnd = end(); + int index = 0; + for ( ; it != theEnd; ++it, ++index ) + delete *it; +} + +// THIS IS AN ATTEMPT TO HIDE THE POLYMORPHISM INSIDE THE LIST +// HOWEVER IT FAILS BECAUSE WE NEED BOTH THE ITERATOR AND THE CASTED Single|MultiField it points to + +SingleField * FieldList::findSingleField( QCString tag ) +{ + FieldListIterator it = begin(); + return findSingleField( it, tag ); +} + +SingleField * FieldList::findSingleField( FieldListIterator &it, QCString tag ) +{ + FieldListIterator found = find( it, tag ); + if ( found == end() ) + return 0; + else + return dynamic_cast<SingleField *>( *found ); +} + +MultiField * FieldList::findMultiField( QCString tag ) +{ + FieldListIterator it = begin(); + return findMultiField( it, tag ); +} + +MultiField * FieldList::findMultiField( FieldListIterator &it, QCString tag ) +{ + FieldListIterator found = find( it, tag ); + if ( found == end() ) + return 0; + else + return dynamic_cast<MultiField *>( *found ); +} + + +/* === FieldBase ========================================================= */ + +FieldBase::FieldBase( QCString tag, Q_UINT8 method, Q_UINT8 flags, Q_UINT8 type ) +: m_tag( tag ), m_method( method ), m_flags( flags ), m_type( type ) +{ + +} + +QCString FieldBase::tag() const +{ + return m_tag; +} + +Q_UINT8 FieldBase::method() const +{ + return m_method; +} + +Q_UINT8 FieldBase::flags() const +{ + return m_flags; +} + +Q_UINT8 FieldBase::type() const +{ + return m_type; +} + +void FieldBase::setFlags( const Q_UINT8 flags ) +{ + m_flags = flags; +} + +/* === SingleField ========================================================= */ + +SingleField::SingleField( QCString tag, Q_UINT8 method, Q_UINT8 flags, Q_UINT8 type, QVariant value ) +: FieldBase( tag, method, flags, type ), m_value( value ) +{ +} + +SingleField::SingleField( QCString tag, Q_UINT8 flags, Q_UINT8 type, QVariant value ) +: FieldBase( tag, NMFIELD_METHOD_VALID, flags, type ), m_value( value ) +{ +} + +SingleField::~SingleField() +{ +} + +void SingleField::setValue( const QVariant v ) +{ + m_value = v; +} + +QVariant SingleField::value() const +{ + return m_value; +} + +/* === MultiField ========================================================= */ + +MultiField::MultiField( QCString tag, Q_UINT8 method, Q_UINT8 flags, Q_UINT8 type, FieldList fields ) +: FieldBase( tag, method, flags, type ), m_fields( fields ) +{ +} + +MultiField::MultiField( QCString tag, Q_UINT8 method, Q_UINT8 flags, Q_UINT8 type ) +: FieldBase( tag, method, flags, type ) +{ +} + +MultiField::~MultiField() +{ + m_fields.purge(); +} + +FieldList MultiField::fields() const +{ + return m_fields; +} + +void MultiField::setFields( FieldList fields ) +{ + m_fields = fields; +} diff --git a/kopete/protocols/groupwise/libgroupwise/gwfield.h b/kopete/protocols/groupwise/libgroupwise/gwfield.h new file mode 100644 index 00000000..9362b5ad --- /dev/null +++ b/kopete/protocols/groupwise/libgroupwise/gwfield.h @@ -0,0 +1,275 @@ +/* + gwfield.h - Fields used for Request/Response data in GroupWise + + Copyright (c) 2004 SUSE Linux AG http://www.suse.com + + Based on Testbed + Copyright (c) 2003 by Will Stephenson <will@stevello.free-online.co.uk> + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#ifndef GWFIELD_H +#define GWFIELD_H + +/* Field types */ +/* Comments: ^1 not used ^2 ignored ^3 apparently only used in _field_to_string for debug */ +/* Otherwise: widely used */ +#define NMFIELD_TYPE_INVALID 0 +/* ^1 */ +#define NMFIELD_TYPE_NUMBER 1 +/* ^1 */ +#define NMFIELD_TYPE_BINARY 2 +/* ^2? */ +#define NMFIELD_TYPE_BYTE 3 +/* ^3 */ +#define NMFIELD_TYPE_UBYTE 4 +/* ^3 */ +#define NMFIELD_TYPE_WORD 5 +/* ^3 */ +#define NMFIELD_TYPE_UWORD 6 +/* ^3 */ +#define NMFIELD_TYPE_DWORD 7 +/* ^3 */ +#define NMFIELD_TYPE_UDWORD 8 +/*WILLNOTE used in nm_send_login ( build ID ) and nm_send_message ( message type = 0 ) */ +#define NMFIELD_TYPE_ARRAY 9 +#define NMFIELD_TYPE_UTF8 10 +#define NMFIELD_TYPE_BOOL 11 +/* ^3 */ +#define NMFIELD_TYPE_MV 12 +#define NMFIELD_TYPE_DN 13 + +/* Field methods */ +#define NMFIELD_METHOD_VALID 0 +#define NMFIELD_METHOD_IGNORE 1 +#define NMFIELD_METHOD_DELETE 2 +#define NMFIELD_METHOD_DELETE_ALL 3 +#define NMFIELD_METHOD_EQUAL 4 +#define NMFIELD_METHOD_ADD 5 +#define NMFIELD_METHOD_UPDATE 6 +#define NMFIELD_METHOD_GTE 10 +#define NMFIELD_METHOD_LTE 12 +#define NMFIELD_METHOD_NE 14 +#define NMFIELD_METHOD_EXIST 15 +#define NMFIELD_METHOD_NOTEXIST 16 +#define NMFIELD_METHOD_SEARCH 17 +#define NMFIELD_METHOD_MATCHBEGIN 19 +#define NMFIELD_METHOD_MATCHEND 20 +#define NMFIELD_METHOD_NOT_ARRAY 40 +#define NMFIELD_METHOD_OR_ARRAY 41 +#define NMFIELD_METHOD_AND_ARRAY 42 + +/* Attribute Names (field tags) */ +#define NM_A_IP_ADDRESS "nnmIPAddress" +#define NM_A_PORT "nnmPort" +#define NM_A_FA_FOLDER "NM_A_FA_FOLDER" +#define NM_A_FA_CONTACT "NM_A_FA_CONTACT" +#define NM_A_FA_CONVERSATION "NM_A_FA_CONVERSATION" +#define NM_A_FA_MESSAGE "NM_A_FA_MESSAGE" +#define NM_A_FA_CONTACT_LIST "NM_A_FA_CONTACT_LIST" +#define NM_A_FA_RESULTS "NM_A_FA_RESULTS" +#define NM_A_FA_INFO_DISPLAY_ARRAY "NM_A_FA_INFO_DISPLAY_ARRAY" +#define NM_A_FA_USER_DETAILS "NM_A_FA_USER_DETAILS" +#define NM_A_SZ_OBJECT_ID "NM_A_SZ_OBJECT_ID" +#define NM_A_SZ_PARENT_ID "NM_A_SZ_PARENT_ID" +#define NM_A_SZ_SEQUENCE_NUMBER "NM_A_SZ_SEQUENCE_NUMBER" +#define NM_A_SZ_TYPE "NM_A_SZ_TYPE" +#define NM_A_SZ_STATUS "NM_A_SZ_STATUS" +#define NM_A_SZ_STATUS_TEXT "NM_A_SZ_STATUS_TEXT" +#define NM_A_SZ_DN "NM_A_SZ_DN" +#define NM_A_SZ_DISPLAY_NAME "NM_A_SZ_DISPLAY_NAME" +#define NM_A_SZ_USERID "NM_A_SZ_USERID" +#define NM_A_SZ_CREDENTIALS "NM_A_SZ_CREDENTIALS" +#define NM_A_SZ_MESSAGE_BODY "NM_A_SZ_MESSAGE_BODY" +#define NM_A_SZ_MESSAGE_TEXT "NM_A_SZ_MESSAGE_TEXT" +#define NM_A_UD_MESSAGE_TYPE "NM_A_UD_MESSAGE_TYPE" +#define NM_A_FA_PARTICIPANTS "NM_A_FA_PARTICIPANTS" +#define NM_A_FA_INVITES "NM_A_FA_INVITES" +#define NM_A_FA_EVENT "NM_A_FA_EVENT" +#define NM_A_UD_COUNT "NM_A_UD_COUNT" +#define NM_A_UD_DATE "NM_A_UD_DATE" +#define NM_A_UD_EVENT "NM_A_UD_EVENT" +#define NM_A_B_NO_CONTACTS "NM_A_B_NO_CONTACTS" +#define NM_A_B_NO_CUSTOMS "NM_A_B_NO_CUSTOMS" +#define NM_A_B_NO_PRIVACY "NM_A_B_NO_PRIVACY" +#define NM_A_B_ONLY_MODIFIED "NM_A_B_ONLY_MODIFIED" +#define NM_A_UW_STATUS "NM_A_UW_STATUS" +#define NM_A_UD_OBJECT_ID "NM_A_UD_OBJECT_ID" +#define NM_A_SZ_TRANSACTION_ID "NM_A_SZ_TRANSACTION_ID" +#define NM_A_SZ_RESULT_CODE "NM_A_SZ_RESULT_CODE" +#define NM_A_UD_BUILD "NM_A_UD_BUILD" +#define NM_A_SZ_AUTH_ATTRIBUTE "NM_A_SZ_AUTH_ATTRIBUTE" +#define NM_A_UD_KEEPALIVE "NM_A_UD_KEEPALIVE" +#define NM_A_SZ_USER_AGENT "NM_A_SZ_USER_AGENT" +#define NM_A_BLOCKING "nnmBlocking" +#define NM_A_BLOCKING_DENY_LIST "nnmBlockingDenyList" +#define NM_A_BLOCKING_ALLOW_LIST "nnmBlockingAllowList" +#define NM_A_SZ_BLOCKING_ALLOW_ITEM "NM_A_SZ_BLOCKING_ALLOW_ITEM" +#define NM_A_SZ_BLOCKING_DENY_ITEM "NM_A_SZ_BLOCKING_DENY_ITEM" +#define NM_A_LOCKED_ATTR_LIST "nnmLockedAttrList" +#define NM_A_SZ_DEPARTMENT "OU" +#define NM_A_SZ_TITLE "Title" +// GW7 +#define NM_A_FA_CUSTOM_STATUSES "NM_A_FA_CUSTOM_STATUSES" +#define NM_A_FA_STATUS "NM_A_FA_STATUS" +#define NM_A_UD_QUERY_COUNT "NM_A_UD_QUERY_COUNT" +#define NM_A_FA_CHAT "NM_A_FA_CHAT" +#define NM_A_DISPLAY_NAME "nnmDisplayName" +#define NM_A_CHAT_OWNER_DN "nnmChatOwnerDN" +#define NM_A_UD_PARTICIPANTS "NM_A_UD_PARTICIPANTS" +#define NM_A_DESCRIPTION "nnmDescription" +#define NM_A_DISCLAIMER "nnmDisclaimer" +#define NM_A_QUERY "nnmQuery" +#define NM_A_ARCHIVE "nnmArchive" +#define NM_A_MAX_USERS "nnmMaxUsers" +#define NM_A_SZ_TOPIC "NM_A_SZ_TOPIC" +#define NM_A_FA_CHAT_ACL "NM_A_FA_CHAT_ACL" +#define NM_A_FA_CHAT_ACL_ENTRY "NM_A_FA_CHAT_ACL_ENTRY" +#define NM_A_SZ_ACCESS_FLAGS "NM_A_SZ_ACCESS_FLAGS" +#define NM_A_CHAT_CREATOR_DN "nnmCreatorDN" +#define NM_A_CREATION_TIME "nnmCreationTime" +#define NM_A_UD_CHAT_RIGHTS "NM_A_UD_CHAT_RIGHTS" + +#define NM_PROTOCOL_VERSION 5 +#define NM_FIELD_TRUE "1" +#define NM_FIELD_FALSE "0" + +#define NMFIELD_MAX_STR_LENGTH 32768 + +#include <qglobal.h> +#include <qobject.h> +#include <qvariant.h> +#include <qvaluelist.h> + +/** + * Fields are typed units of information interchanged between the groupwise server and its clients. + * In this implementation Fields are assumed to have a straight data flow from a Task to a socket and vice versa, + * so the @ref Task::take() is responsible for deleting incoming Fields and the netcode is responsible for + * deleting outgoing Fields. + */ + +namespace Field +{ + /** + * Abstract base class of all field types + */ + class FieldBase + { + public: + FieldBase() {} + FieldBase( QCString tag, Q_UINT8 method, Q_UINT8 flags, Q_UINT8 type ); + virtual ~FieldBase() {} + QCString tag() const; + Q_UINT8 method() const; + Q_UINT8 flags() const; + Q_UINT8 type() const; + void setFlags( const Q_UINT8 flags ); + protected: + QCString m_tag; + Q_UINT8 m_method; + Q_UINT8 m_flags; + Q_UINT8 m_type; // doch needed + }; + + typedef QValueListIterator<FieldBase *> FieldListIterator; + typedef QValueListConstIterator<FieldBase *> FieldListConstIterator; + class SingleField; + class MultiField; + + class FieldList : public QValueList<FieldBase *> + { + public: + /** + * Destructor - doesn't delete the fields because FieldLists are passed by value + */ + virtual ~FieldList(); + /** + * Locate the first occurrence of a given field in the list. Same semantics as QValueList::find(). + * @param tag The tag name of the field to search for. + * @return An iterator pointing to the first occurrence found, or end() if none was found. + */ + FieldListIterator find( QCString tag ); + /** + * Locate the first occurrence of a given field in the list, starting at the supplied iterator + * @param tag The tag name of the field to search for. + * @param it An iterator within the list, to start searching from. + * @return An iterator pointing to the first occurrence found, or end() if none was found. + */ + FieldListIterator find( FieldListIterator &it, QCString tag ); + /** + * Get the index of the first occurrence of tag, or -1 if not found + */ + int findIndex( QCString tag ); + /** + * Debug function, dumps to stdout + */ + void dump( bool recursive = false, int offset = 0 ); + /** + * Delete the contents of the list + */ + void purge(); + /** + * Utility functions for finding the first instance of a tag + * @return 0 if no field of the right tag and type was found. + */ + SingleField * findSingleField( QCString tag ); + MultiField * findMultiField( QCString tag ); + protected: + SingleField * findSingleField( FieldListIterator &it, QCString tag ); + MultiField * findMultiField( FieldListIterator &it, QCString tag ); + + }; + + /** + * This class is responsible for storing all Groupwise single value field types, eg + * NMFIELD_TYPE_INVALID, NMFIELD_TYPE_NUMBER, NMFIELD_TYPE_BINARY, NMFIELD_TYPE_BYTE + * NMFIELD_TYPE_UBYTE, NMFIELD_TYPE_DWORD, NMFIELD_TYPE_UDWORD, NMFIELD_TYPE_UTF8, NMFIELD_TYPE_BOOL + * NMFIELD_TYPE_DN + */ + class SingleField : public FieldBase + { + public: + /** + * Single field constructor + */ + SingleField( QCString tag, Q_UINT8 method, Q_UINT8 flags, Q_UINT8 type, QVariant value ); + /** + * Convenience constructor for NMFIELD_METHOD_VALID fields + */ + SingleField( QCString tag, Q_UINT8 flags, Q_UINT8 type, QVariant value ); + ~SingleField(); + void setValue( const QVariant v ); + QVariant value() const; + private: + QVariant m_value; + }; + + /** + * This class is responsible for storing multi-value GroupWise field types, eg + * NMFIELD_TYPE_ARRAY, NMFIELD_TYPE_MV + */ + class MultiField : public FieldBase + { + public: + MultiField( QCString tag, Q_UINT8 method, Q_UINT8 flags, Q_UINT8 type ); + MultiField( QCString tag, Q_UINT8 method, Q_UINT8 flags, Q_UINT8 type, FieldList fields ); + ~MultiField(); + FieldList fields() const; + void setFields( FieldList ); + private: + FieldList m_fields; // nb implicitly shared, copy-on-write - is there a case where this is bad? + }; + +} + +#endif diff --git a/kopete/protocols/groupwise/libgroupwise/gwglobal.cpp b/kopete/protocols/groupwise/libgroupwise/gwglobal.cpp new file mode 100644 index 00000000..4ea25779 --- /dev/null +++ b/kopete/protocols/groupwise/libgroupwise/gwglobal.cpp @@ -0,0 +1,39 @@ +/* + gwglobal.cpp - Kopete Groupwise Protocol + + Copyright (c) 2004 SUSE Linux AG http://www.suse.com + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#include "gwerror.h" + +namespace GroupWise +{ + ConferenceGuid::ConferenceGuid() {} + ConferenceGuid::ConferenceGuid( const QString & string ) : QString( string ) {} + + ConferenceGuid::~ConferenceGuid() {} + + bool operator==( const ConferenceGuid & g1, const ConferenceGuid & g2 ) + { + return g1.left( CONF_GUID_END ) == g2.left( CONF_GUID_END ); + } + bool operator==( const QString & s, const ConferenceGuid & g ) + { + return s.left( CONF_GUID_END ) == g.left( CONF_GUID_END ); + } + bool operator==( const ConferenceGuid & g, const QString & s ) + { + return s.left( CONF_GUID_END ) == g.left( CONF_GUID_END ); + } +} diff --git a/kopete/protocols/groupwise/libgroupwise/inputprotocolbase.cpp b/kopete/protocols/groupwise/libgroupwise/inputprotocolbase.cpp new file mode 100644 index 00000000..30627a81 --- /dev/null +++ b/kopete/protocols/groupwise/libgroupwise/inputprotocolbase.cpp @@ -0,0 +1,112 @@ +/* + Kopete Groupwise Protocol + inputprotocolbase.cpp - Ancestor of all protocols used for reading GroupWise input + + Copyright (c) 2004 SUSE Linux AG http://www.suse.com + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#include <kdebug.h> + +#include "gwerror.h" + +#include "gwfield.h" +#include "inputprotocolbase.h" + +InputProtocolBase::InputProtocolBase(QObject *parent, const char *name) + : QObject(parent, name) +{ +} + + +InputProtocolBase::~InputProtocolBase() +{ +} + +void InputProtocolBase::debug( const QString &str ) +{ +#ifdef LIBGW_USE_KDEBUG + kdDebug( 14191 ) << "debug: " << str << endl; +#else + qDebug( "GW RAW PROTO: %s\n", str.ascii() ); +#endif +} + +uint InputProtocolBase::state() const +{ + return m_state; +} + +bool InputProtocolBase::readString( QString &message ) +{ + uint len; + QCString rawData; + if ( !safeReadBytes( rawData, len ) ) + return false; + message = QString::fromUtf8( rawData.data(), len - 1 ); + return true; +} + + +bool InputProtocolBase::okToProceed() +{ + if ( m_din.device() ) + { + if ( m_din.atEnd() ) + { + m_state = NeedMore; + debug( "InputProtocol::okToProceed() - Server message ended prematurely!" ); + } + else + return true; + } + return false; +} + +bool InputProtocolBase::safeReadBytes( QCString & data, uint & len ) +{ + // read the length of the bytes + Q_UINT32 val; + if ( !okToProceed() ) + return false; + m_din >> val; + m_bytes += sizeof( Q_UINT32 ); + if ( val > NMFIELD_MAX_STR_LENGTH ) + return false; + //qDebug( "EventProtocol::safeReadBytes() - expecting %i bytes", val ); + QCString temp( val ); + if ( val != 0 ) + { + if ( !okToProceed() ) + return false; + // if the server splits packets here we are in trouble, + // as there is no way to see how much data was actually read + m_din.readRawBytes( temp.data(), val ); + // the rest of the string will be filled with FF, + // so look for that in the last position instead of \0 + // this caused a crash - guessing that temp.length() is set to the number of bytes actually read... + // if ( (Q_UINT8)( * ( temp.data() + ( temp.length() - 1 ) ) ) == 0xFF ) + if ( temp.length() < ( val - 1 ) ) + { + debug( QString( "InputProtocol::safeReadBytes() - string broke, giving up, only got: %1 bytes out of %2" ).arg( temp.length() ).arg( val ) ); + m_state = NeedMore; + return false; + } + } + data = temp; + len = val; + m_bytes += val; + return true; +} + +#include "inputprotocolbase.moc" diff --git a/kopete/protocols/groupwise/libgroupwise/inputprotocolbase.h b/kopete/protocols/groupwise/libgroupwise/inputprotocolbase.h new file mode 100644 index 00000000..efd2979f --- /dev/null +++ b/kopete/protocols/groupwise/libgroupwise/inputprotocolbase.h @@ -0,0 +1,78 @@ +/* + Kopete Groupwise Protocol + inputprotocolbase.h - Ancestor of all protocols used for reading GroupWise input + + Copyright (c) 2004 SUSE Linux AG http://www.suse.com + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#ifndef INPUTPROTOCOLBASE_H +#define INPUTPROTOCOLBASE_H + +#include <qobject.h> + +class Transfer; +/** +Defines a basic interface for protocols dealing with input from the GroupWise server. + +@author Kopete Developers +*/ +class InputProtocolBase : public QObject +{ +Q_OBJECT +public: + enum EventProtocolState { Success, NeedMore, OutOfSync, ProtocolError }; + InputProtocolBase(QObject *parent = 0, const char *name = 0); + ~InputProtocolBase(); + + /** + * Debug output + */ + static void debug(const QString &str); + + /** + * Returns a value describing the state of the object. + * If the object is given data to parse that does not begin with a recognised event code, + * it will become OutOfSync, to indicate that the input data probably contains leftover data not processed during a previous parse. + */ + uint state() const; + /** + * Attempt to parse the supplied data into a Transfer object + * @param bytes this will be set to the number of bytes that were successfully parsed. It is no indication of the success of the whole procedure + * @return On success, a Transfer object that the caller is responsible for deleting. It will be either an EventTransfer or a Response, delete as appropriate. On failure, returns 0. + */ + virtual Transfer * parse( const QByteArray &, uint & bytes ) = 0 ; +protected: + /** + * Reads an arbitrary string + * updates the bytes parsed counter + */ + bool readString( QString &message ); + /** + * Check that there is data to read, and set the protocol's state if there isn't any. + */ + bool okToProceed(); + /** + * read a Q_UINT32 giving the number of following bytes, then a string of that length + * updates the bytes parsed counter + * @return false if the string was broken or there was no data available at all + */ + bool safeReadBytes( QCString & data, uint & len ); + +protected: + uint m_state; + uint m_bytes; + QDataStream m_din; +}; + +#endif diff --git a/kopete/protocols/groupwise/libgroupwise/privacymanager.cpp b/kopete/protocols/groupwise/libgroupwise/privacymanager.cpp new file mode 100644 index 00000000..3d42207b --- /dev/null +++ b/kopete/protocols/groupwise/libgroupwise/privacymanager.cpp @@ -0,0 +1,251 @@ +/* + Kopete Groupwise Protocol + privacymanager.cpp - stores the user's privacy information and maintains it on the server + + Copyright (c) 2004 SUSE Linux AG http://www.suse.com + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#include "client.h" +#include "tasks/privacyitemtask.h" +#include "userdetailsmanager.h" + +#include "privacymanager.h" + +PrivacyManager::PrivacyManager( Client * client, const char *name) + : QObject(client, name), m_client( client ) +{ +} + +PrivacyManager::~PrivacyManager() +{ +} + +bool PrivacyManager::defaultAllow() +{ + return !m_defaultDeny; +} + +bool PrivacyManager::defaultDeny() +{ + return m_defaultDeny; +} + +QStringList PrivacyManager::allowList() +{ + return m_allowList; +} + +QStringList PrivacyManager::denyList() +{ + return m_denyList; +} + +bool PrivacyManager::isPrivacyLocked() +{ + return m_locked; +} + +bool PrivacyManager::isBlocked( const QString & dn ) +{ + if ( m_defaultDeny ) + return !m_allowList.contains( dn ); + else + return m_denyList.contains( dn ); +} + +void PrivacyManager::setAllow( const QString & dn ) +{ + if ( m_defaultDeny ) + { + if ( !m_allowList.contains( dn ) ) + addAllow( dn ); + } + else + { + if ( m_denyList.contains( dn ) ) + removeDeny( dn ); + } +} + +void PrivacyManager::setDeny( const QString & dn ) +{ + if ( m_defaultDeny ) + { + if ( m_allowList.contains( dn ) ) + removeAllow( dn ); + } + else + { + if ( !m_denyList.contains( dn ) ) + addDeny( dn ); + } +} + + +void PrivacyManager::setDefaultAllow( bool allow ) +{ + PrivacyItemTask * pit = new PrivacyItemTask( m_client->rootTask() ); + pit->defaultPolicy( !allow ); + connect( pit, SIGNAL( finished() ), SLOT( slotDefaultPolicyChanged() ) ); + pit->go( true ); +} + +void PrivacyManager::setDefaultDeny( bool deny ) +{ + PrivacyItemTask * pit = new PrivacyItemTask( m_client->rootTask() ); + pit->defaultPolicy( deny); + connect( pit, SIGNAL( finished() ), SLOT( slotDefaultPolicyChanged() ) ); + pit->go( true ); +} + +void PrivacyManager::addAllow( const QString & dn ) +{ + // start off a CreatePrivacyItemTask + PrivacyItemTask * pit = new PrivacyItemTask( m_client->rootTask() ); + pit->allow( dn ); + connect( pit, SIGNAL( finished() ), SLOT( slotAllowAdded() ) ); + pit->go( true ); +} + +void PrivacyManager::addDeny( const QString & dn ) +{ + // start off a CreatePrivacyItemTask + PrivacyItemTask * pit = new PrivacyItemTask( m_client->rootTask() ); + pit->deny( dn ); + connect( pit, SIGNAL( finished() ), SLOT( slotDenyAdded() ) ); + pit->go( true ); +} + +void PrivacyManager::removeAllow( const QString & dn ) +{ + PrivacyItemTask * pit = new PrivacyItemTask( m_client->rootTask() ); + pit->removeAllow( dn ); + connect( pit, SIGNAL( finished() ), SLOT( slotAllowRemoved() ) ); + pit->go( true ); +} + +void PrivacyManager::removeDeny( const QString & dn ) +{ + // start off a CreatePrivacyItemTask + PrivacyItemTask * pit = new PrivacyItemTask( m_client->rootTask() ); + pit->removeDeny( dn ); + connect( pit, SIGNAL( finished() ), SLOT( slotDenyRemoved() ) ); + pit->go( true ); +} + +void PrivacyManager::setPrivacy( bool defaultIsDeny, const QStringList & allowList, const QStringList & denyList ) +{ + if ( defaultIsDeny != m_defaultDeny ) + setDefaultDeny( defaultIsDeny ); + // find the DNs no longer in the allow list + QStringList allowsToRemove = difference( m_allowList, allowList ); + // find the DNs no longer in the deny list + QStringList denysToRemove = difference( m_denyList, denyList ); + // find the DNs new in the allow list + QStringList allowsToAdd = difference( allowList, m_allowList ); + // find the DNs new in the deny list + QStringList denysToAdd = difference( denyList, m_denyList ); + + QStringList::ConstIterator end = allowsToRemove.end(); + for ( QStringList::ConstIterator it = allowsToRemove.begin(); it != end; ++it ) + removeAllow( *it ); + end = denysToRemove.end(); + for ( QStringList::ConstIterator it = denysToRemove.begin(); it != end; ++it ) + removeDeny( *it ); + end = allowsToAdd.end(); + for ( QStringList::ConstIterator it = allowsToAdd.begin(); it != end; ++it ) + addAllow( *it ); + end = denysToAdd.end(); + for ( QStringList::ConstIterator it = denysToAdd.begin(); it != end; ++it ) + addDeny( *it ); +} + +void PrivacyManager::slotGotPrivacySettings( bool locked, bool defaultDeny, const QStringList & allowList, const QStringList & denyList ) +{ + m_locked = locked; + m_defaultDeny = defaultDeny; + m_allowList = allowList; + m_denyList = denyList; +} + +void PrivacyManager::getDetailsForPrivacyLists() +{ + if ( !m_allowList.isEmpty() ) + { + m_client->userDetailsManager()->requestDetails( m_allowList ); + } + if ( !m_denyList.isEmpty() ) + m_client->userDetailsManager()->requestDetails( m_denyList ); +} + +void PrivacyManager::slotDefaultPolicyChanged() +{ + PrivacyItemTask * pit = ( PrivacyItemTask * )sender(); + if ( pit->success() ) + m_defaultDeny = pit->defaultDeny(); +} + +void PrivacyManager::slotAllowAdded() +{ + PrivacyItemTask * pit = ( PrivacyItemTask * )sender(); + if ( pit->success() ) + { + m_allowList.append( pit->dn() ); + emit privacyChanged( pit->dn(), isBlocked( pit->dn() ) ); + } +} + +void PrivacyManager::slotDenyAdded() +{ + PrivacyItemTask * pit = ( PrivacyItemTask * )sender(); + if ( pit->success() ) + { + m_denyList.append( pit->dn() ); + emit privacyChanged( pit->dn(), isBlocked( pit->dn() ) ); + } +} + +void PrivacyManager::slotAllowRemoved() +{ + PrivacyItemTask * pit = ( PrivacyItemTask * )sender(); + if ( pit->success() ) + { + m_allowList.remove( pit->dn() ); + emit privacyChanged( pit->dn(), isBlocked( pit->dn() ) ); + } +} + +void PrivacyManager::slotDenyRemoved() +{ + PrivacyItemTask * pit = ( PrivacyItemTask * )sender(); + if ( pit->success() ) + { + m_denyList.remove( pit->dn() ); + emit privacyChanged( pit->dn(), isBlocked( pit->dn() ) ); + } +} + +QStringList PrivacyManager::difference( const QStringList & lhs, const QStringList & rhs ) +{ + QStringList diff; + const QStringList::ConstIterator lhsEnd = lhs.end(); + const QStringList::ConstIterator rhsEnd = rhs.end(); + for ( QStringList::ConstIterator lhsIt = lhs.begin(); lhsIt != lhsEnd; ++lhsIt ) + { + if ( rhs.find( *lhsIt ) == rhsEnd ) + diff.append( *lhsIt ); + } + return diff; +} +#include "privacymanager.moc" diff --git a/kopete/protocols/groupwise/libgroupwise/privacymanager.h b/kopete/protocols/groupwise/libgroupwise/privacymanager.h new file mode 100644 index 00000000..102c2b0a --- /dev/null +++ b/kopete/protocols/groupwise/libgroupwise/privacymanager.h @@ -0,0 +1,88 @@ +/* + Kopete Groupwise Protocol + privacymanager.cpp - stores the user's privacy information and maintains it on the server + + Copyright (c) 2004 SUSE Linux AG http://www.suse.com + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#ifndef PRIVACYMANAGER_H +#define PRIVACYMANAGER_H + +#include <qobject.h> +#include <qstringlist.h> + +class Client; + +/** +Keeps a record of the server side privacy allow and deny lists, default policy and whether the user is allowed to change privacy settings + +@author SUSE AG +*/ +class PrivacyManager : public QObject +{ +Q_OBJECT +public: + PrivacyManager( Client * client, const char *name = 0); + ~PrivacyManager(); + // accessors + bool isBlocked( const QString & dn ); + QStringList allowList(); + QStringList denyList(); + bool isPrivacyLocked(); + bool defaultDeny(); + bool defaultAllow(); + // mutators + void setDefaultAllow( bool allow ); + void setDefaultDeny( bool deny ); + void setAllow( const QString & dn ); + void setDeny( const QString & dn ); + void getDetailsForPrivacyLists(); + // change everything at once + void setPrivacy( bool defaultIsDeny, const QStringList & allowList, const QStringList & denyList ); + +signals: + void privacyChanged( const QString &dn, bool allowed ); +public slots: + /** + * Used to initialise the privacy manager using the server side privacy list + */ + void slotGotPrivacySettings( bool locked, bool defaultDeny, const QStringList & allowList, const QStringList & denyList ); +protected: + void addAllow( const QString & dn ); + void addDeny( const QString & dn ); + void removeAllow( const QString & dn ); + void removeDeny( const QString & dn ); + /** + * A set difference function + * @param lhs The set of strings to be subtracted from + * @param rhs The set of string to subtract + * @return The difference between the two sets + */ + QStringList difference( const QStringList & lhs, const QStringList & rhs ); +protected slots: + // Receive the results of Tasks manipulating the privacy lists + void slotDefaultPolicyChanged(); + void slotAllowAdded(); + void slotDenyAdded(); + void slotAllowRemoved(); + void slotDenyRemoved(); +private: + Client * m_client; + bool m_locked; + bool m_defaultDeny; + QStringList m_allowList; + QStringList m_denyList; +}; + +#endif diff --git a/kopete/protocols/groupwise/libgroupwise/qca/COPYING b/kopete/protocols/groupwise/libgroupwise/qca/COPYING new file mode 100644 index 00000000..b1e3f5a2 --- /dev/null +++ b/kopete/protocols/groupwise/libgroupwise/qca/COPYING @@ -0,0 +1,504 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + <one line to give the library's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + <signature of Ty Coon>, 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! + + diff --git a/kopete/protocols/groupwise/libgroupwise/qca/INSTALL b/kopete/protocols/groupwise/libgroupwise/qca/INSTALL new file mode 100644 index 00000000..8dd34099 --- /dev/null +++ b/kopete/protocols/groupwise/libgroupwise/qca/INSTALL @@ -0,0 +1,12 @@ +Installing QCA +-------------- + +Installation should be straightforward: + + ./configure + make + make install + +NOTE: You may also need to run '/sbin/ldconfig' or a similar tool to + get the new library files recognized by the system. If you are + using Linux, just run it for good measure. diff --git a/kopete/protocols/groupwise/libgroupwise/qca/Makefile.am b/kopete/protocols/groupwise/libgroupwise/qca/Makefile.am new file mode 100644 index 00000000..af437a64 --- /dev/null +++ b/kopete/protocols/groupwise/libgroupwise/qca/Makefile.am @@ -0,0 +1 @@ +SUBDIRS = src diff --git a/kopete/protocols/groupwise/libgroupwise/qca/README b/kopete/protocols/groupwise/libgroupwise/qca/README new file mode 100644 index 00000000..0641713a --- /dev/null +++ b/kopete/protocols/groupwise/libgroupwise/qca/README @@ -0,0 +1,29 @@ +Qt Cryptographic Architecture +----------------------------- +Version: API v1.0, Plugin v1 +Author: Justin Karneges <justin@affinix.com> +Date: September 10th 2003 + +This library provides an easy API for the following features: + + SSL/TLS + X509 + SASL + RSA + Hashing (SHA1, MD5) + Ciphers (BlowFish, 3DES, AES) + +Functionality is supplied via plugins. This is useful for avoiding +dependence on a particular crypto library and makes upgrading easier, +as there is no need to recompile your application when adding or +upgrading a crypto plugin. Also, by pushing crypto functionality into +plugins, your application is free of legal issues, such as export +regulation. + +And of course, you get a very simple crypto API for Qt, where you can +do things like: + + QString hash = QCA::SHA1::hashToString(blockOfData); + +Have fun! + diff --git a/kopete/protocols/groupwise/libgroupwise/qca/TODO b/kopete/protocols/groupwise/libgroupwise/qca/TODO new file mode 100644 index 00000000..bc8247e0 --- /dev/null +++ b/kopete/protocols/groupwise/libgroupwise/qca/TODO @@ -0,0 +1,6 @@ +* plugins: thread safety ? + +* dsa +* diffie-hellman +* entropy + diff --git a/kopete/protocols/groupwise/libgroupwise/qca/src/Makefile.am b/kopete/protocols/groupwise/libgroupwise/qca/src/Makefile.am new file mode 100644 index 00000000..b7ae1bb4 --- /dev/null +++ b/kopete/protocols/groupwise/libgroupwise/qca/src/Makefile.am @@ -0,0 +1,8 @@ +METASOURCES = AUTO + +noinst_LTLIBRARIES = libqca.la +INCLUDES = $(all_includes) + +libqca_la_SOURCES = \ + qca.cpp +libqca_la_LIBADD = -lqt-mt diff --git a/kopete/protocols/groupwise/libgroupwise/qca/src/qca.cpp b/kopete/protocols/groupwise/libgroupwise/qca/src/qca.cpp new file mode 100644 index 00000000..9edb0fb3 --- /dev/null +++ b/kopete/protocols/groupwise/libgroupwise/qca/src/qca.cpp @@ -0,0 +1,1486 @@ +/* + * qca.cpp - Qt Cryptographic Architecture + * Copyright (C) 2003 Justin Karneges + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include"qca.h" + +#include<qptrlist.h> +#include<qdir.h> +#include<qfileinfo.h> +#include<qstringlist.h> +#include<qlibrary.h> +#include<qtimer.h> +#include<qhostaddress.h> +#include<qapplication.h> +#include<qguardedptr.h> +#include<stdlib.h> +#include"qcaprovider.h" + +#if defined(Q_OS_WIN32) +#define PLUGIN_EXT "dll" +#elif defined(Q_OS_MAC) +#define PLUGIN_EXT "dylib" +#else +#define PLUGIN_EXT "so" +#endif + +using namespace QCA; + +class ProviderItem +{ +public: + QCAProvider *p; + QString fname; + + static ProviderItem *load(const QString &fname) + { + QLibrary *lib = new QLibrary(fname); + if(!lib->load()) { + delete lib; + return 0; + } + void *s = lib->resolve("createProvider"); + if(!s) { + delete lib; + return 0; + } + QCAProvider *(*createProvider)() = (QCAProvider *(*)())s; + QCAProvider *p = createProvider(); + if(!p) { + delete lib; + return 0; + } + ProviderItem *i = new ProviderItem(lib, p); + i->fname = fname; + return i; + } + + static ProviderItem *fromClass(QCAProvider *p) + { + ProviderItem *i = new ProviderItem(0, p); + return i; + } + + ~ProviderItem() + { + delete p; + delete lib; + } + + void ensureInit() + { + if(init_done) + return; + init_done = true; + p->init(); + } + +private: + QLibrary *lib; + bool init_done; + + ProviderItem(QLibrary *_lib, QCAProvider *_p) + { + lib = _lib; + p = _p; + init_done = false; + } +}; + +static QPtrList<ProviderItem> providerList; +static bool qca_init = false; + +static bool plugin_have(const QString &fname) +{ + QPtrListIterator<ProviderItem> it(providerList); + for(ProviderItem *i; (i = it.current()); ++it) { + if(i->fname == fname) + return true; + } + return false; +} + +static void plugin_scan() +{ + QStringList dirs = QApplication::libraryPaths(); + for(QStringList::ConstIterator it = dirs.begin(); it != dirs.end(); ++it) { + QDir libpath(*it); + QDir dir(libpath.filePath("crypto")); + if(!dir.exists()) + continue; + + QStringList list = dir.entryList(); + for(QStringList::ConstIterator it = list.begin(); it != list.end(); ++it) { + QFileInfo fi(dir.filePath(*it)); + if(fi.isDir()) + continue; + if(fi.extension() != PLUGIN_EXT) + continue; + QString fname = fi.filePath(); + + // don't load the same plugin again! + if(plugin_have(fname)) + continue; + //printf("f=[%s]\n", fname.latin1()); + + ProviderItem *i = ProviderItem::load(fname); + if(!i) + continue; + if(i->p->qcaVersion() != QCA_PLUGIN_VERSION) { + delete i; + continue; + } + + providerList.append(i); + } + } +} + +static void plugin_addClass(QCAProvider *p) +{ + ProviderItem *i = ProviderItem::fromClass(p); + providerList.prepend(i); +} + +static void plugin_unloadall() +{ + providerList.clear(); +} + +static int plugin_caps() +{ + int caps = 0; + QPtrListIterator<ProviderItem> it(providerList); + for(ProviderItem *i; (i = it.current()); ++it) + caps |= i->p->capabilities(); + return caps; +} + +QString QCA::arrayToHex(const QByteArray &a) +{ + QString out; + for(int n = 0; n < (int)a.size(); ++n) { + QString str; + str.sprintf("%02x", (uchar)a[n]); + out.append(str); + } + return out; +} + +QByteArray QCA::hexToArray(const QString &str) +{ + QByteArray out(str.length() / 2); + int at = 0; + for(int n = 0; n + 1 < (int)str.length(); n += 2) { + uchar a = str[n]; + uchar b = str[n+1]; + uchar c = ((a & 0x0f) << 4) + (b & 0x0f); + out[at++] = c; + } + return out; +} + +void QCA::init() +{ + if(qca_init) + return; + qca_init = true; + providerList.setAutoDelete(true); +} + +bool QCA::isSupported(int capabilities) +{ + init(); + + int caps = plugin_caps(); + if(caps & capabilities) + return true; + + // ok, try scanning for new stuff + plugin_scan(); + caps = plugin_caps(); + if(caps & capabilities) + return true; + + return false; +} + +void QCA::insertProvider(QCAProvider *p) +{ + plugin_addClass(p); +} + +void QCA::unloadAllPlugins() +{ + plugin_unloadall(); +} + +static void *getContext(int cap) +{ + init(); + + // this call will also trip a scan for new plugins if needed + if(!QCA::isSupported(cap)) + return 0; + + QPtrListIterator<ProviderItem> it(providerList); + for(ProviderItem *i; (i = it.current()); ++it) { + if(i->p->capabilities() & cap) { + i->ensureInit(); + return i->p->context(cap); + } + } + return 0; +} + + +//---------------------------------------------------------------------------- +// Hash +//---------------------------------------------------------------------------- +class Hash::Private +{ +public: + Private() + { + c = 0; + } + + ~Private() + { + delete c; + } + + void reset() + { + c->reset(); + } + + QCA_HashContext *c; +}; + +Hash::Hash(QCA_HashContext *c) +{ + d = new Private; + d->c = c; +} + +Hash::Hash(const Hash &from) +{ + d = new Private; + *this = from; +} + +Hash & Hash::operator=(const Hash &from) +{ + delete d->c; + d->c = from.d->c->clone(); + return *this; +} + +Hash::~Hash() +{ + delete d; +} + +void Hash::clear() +{ + d->reset(); +} + +void Hash::update(const QByteArray &a) +{ + d->c->update(a.data(), a.size()); +} + +QByteArray Hash::final() +{ + QByteArray buf; + d->c->final(&buf); + return buf; +} + + +//---------------------------------------------------------------------------- +// Cipher +//---------------------------------------------------------------------------- +class Cipher::Private +{ +public: + Private() + { + c = 0; + } + + ~Private() + { + delete c; + } + + void reset() + { + dir = Encrypt; + key.resize(0); + iv.resize(0); + err = false; + } + + QCA_CipherContext *c; + int dir; + int mode; + QByteArray key, iv; + bool err; +}; + +Cipher::Cipher(QCA_CipherContext *c, int dir, int mode, const QByteArray &key, const QByteArray &iv, bool pad) +{ + d = new Private; + d->c = c; + reset(dir, mode, key, iv, pad); +} + +Cipher::Cipher(const Cipher &from) +{ + d = new Private; + *this = from; +} + +Cipher & Cipher::operator=(const Cipher &from) +{ + delete d->c; + d->c = from.d->c->clone(); + d->dir = from.d->dir; + d->mode = from.d->mode; + d->key = from.d->key.copy(); + d->iv = from.d->iv.copy(); + d->err = from.d->err; + return *this; +} + +Cipher::~Cipher() +{ + delete d; +} + +QByteArray Cipher::dyn_generateKey(int size) const +{ + QByteArray buf; + if(size != -1) + buf.resize(size); + else + buf.resize(d->c->keySize()); + if(!d->c->generateKey(buf.data(), size)) + return QByteArray(); + return buf; +} + +QByteArray Cipher::dyn_generateIV() const +{ + QByteArray buf(d->c->blockSize()); + if(!d->c->generateIV(buf.data())) + return QByteArray(); + return buf; +} + +void Cipher::reset(int dir, int mode, const QByteArray &key, const QByteArray &iv, bool pad) +{ + d->reset(); + + d->dir = dir; + d->mode = mode; + d->key = key.copy(); + d->iv = iv.copy(); + if(!d->c->setup(d->dir, d->mode, d->key.isEmpty() ? 0: d->key.data(), d->key.size(), d->iv.isEmpty() ? 0 : d->iv.data(), pad)) { + d->err = true; + return; + } +} + +bool Cipher::update(const QByteArray &a) +{ + if(d->err) + return false; + + if(!a.isEmpty()) { + if(!d->c->update(a.data(), a.size())) { + d->err = true; + return false; + } + } + return true; +} + +QByteArray Cipher::final(bool *ok) +{ + if(ok) + *ok = false; + if(d->err) + return QByteArray(); + + QByteArray out; + if(!d->c->final(&out)) { + d->err = true; + return QByteArray(); + } + if(ok) + *ok = true; + return out; +} + + +//---------------------------------------------------------------------------- +// SHA1 +//---------------------------------------------------------------------------- +SHA1::SHA1() +:Hash((QCA_HashContext *)getContext(CAP_SHA1)) +{ +} + + +//---------------------------------------------------------------------------- +// SHA256 +//---------------------------------------------------------------------------- +SHA256::SHA256() +:Hash((QCA_HashContext *)getContext(CAP_SHA256)) +{ +} + + +//---------------------------------------------------------------------------- +// MD5 +//---------------------------------------------------------------------------- +MD5::MD5() +:Hash((QCA_HashContext *)getContext(CAP_MD5)) +{ +} + + +//---------------------------------------------------------------------------- +// BlowFish +//---------------------------------------------------------------------------- +BlowFish::BlowFish(int dir, int mode, const QByteArray &key, const QByteArray &iv, bool pad) +:Cipher((QCA_CipherContext *)getContext(CAP_BlowFish), dir, mode, key, iv, pad) +{ +} + + +//---------------------------------------------------------------------------- +// TripleDES +//---------------------------------------------------------------------------- +TripleDES::TripleDES(int dir, int mode, const QByteArray &key, const QByteArray &iv, bool pad) +:Cipher((QCA_CipherContext *)getContext(CAP_TripleDES), dir, mode, key, iv, pad) +{ +} + + +//---------------------------------------------------------------------------- +// AES128 +//---------------------------------------------------------------------------- +AES128::AES128(int dir, int mode, const QByteArray &key, const QByteArray &iv, bool pad) +:Cipher((QCA_CipherContext *)getContext(CAP_AES128), dir, mode, key, iv, pad) +{ +} + + +//---------------------------------------------------------------------------- +// AES256 +//---------------------------------------------------------------------------- +AES256::AES256(int dir, int mode, const QByteArray &key, const QByteArray &iv, bool pad) +:Cipher((QCA_CipherContext *)getContext(CAP_AES256), dir, mode, key, iv, pad) +{ +} + + +//---------------------------------------------------------------------------- +// RSAKey +//---------------------------------------------------------------------------- +class RSAKey::Private +{ +public: + Private() + { + c = 0; + } + + ~Private() + { + delete c; + } + + QCA_RSAKeyContext *c; +}; + +RSAKey::RSAKey() +{ + d = new Private; + d->c = (QCA_RSAKeyContext *)getContext(CAP_RSA); +} + +RSAKey::RSAKey(const RSAKey &from) +{ + d = new Private; + *this = from; +} + +RSAKey & RSAKey::operator=(const RSAKey &from) +{ + delete d->c; + d->c = from.d->c->clone(); + return *this; +} + +RSAKey::~RSAKey() +{ + delete d; +} + +bool RSAKey::isNull() const +{ + return d->c->isNull(); +} + +bool RSAKey::havePublic() const +{ + return d->c->havePublic(); +} + +bool RSAKey::havePrivate() const +{ + return d->c->havePrivate(); +} + +QByteArray RSAKey::toDER(bool publicOnly) const +{ + QByteArray out; + if(!d->c->toDER(&out, publicOnly)) + return QByteArray(); + return out; +} + +bool RSAKey::fromDER(const QByteArray &a) +{ + return d->c->createFromDER(a.data(), a.size()); +} + +QString RSAKey::toPEM(bool publicOnly) const +{ + QByteArray out; + if(!d->c->toPEM(&out, publicOnly)) + return QByteArray(); + + QCString cs; + cs.resize(out.size()+1); + memcpy(cs.data(), out.data(), out.size()); + return QString::fromLatin1(cs); +} + +bool RSAKey::fromPEM(const QString &str) +{ + QCString cs = str.latin1(); + QByteArray a(cs.length()); + memcpy(a.data(), cs.data(), a.size()); + return d->c->createFromPEM(a.data(), a.size()); +} + +bool RSAKey::fromNative(void *p) +{ + return d->c->createFromNative(p); +} + +bool RSAKey::encrypt(const QByteArray &a, QByteArray *b, bool oaep) const +{ + QByteArray out; + if(!d->c->encrypt(a, &out, oaep)) + return false; + *b = out; + return true; +} + +bool RSAKey::decrypt(const QByteArray &a, QByteArray *b, bool oaep) const +{ + QByteArray out; + if(!d->c->decrypt(a, &out, oaep)) + return false; + *b = out; + return true; +} + +bool RSAKey::generate(unsigned int bits) +{ + return d->c->generate(bits); +} + + +//---------------------------------------------------------------------------- +// RSA +//---------------------------------------------------------------------------- +RSA::RSA() +{ +} + +RSA::~RSA() +{ +} + +RSAKey RSA::key() const +{ + return v_key; +} + +void RSA::setKey(const RSAKey &k) +{ + v_key = k; +} + +bool RSA::encrypt(const QByteArray &a, QByteArray *b, bool oaep) const +{ + if(v_key.isNull()) + return false; + return v_key.encrypt(a, b, oaep); +} + +bool RSA::decrypt(const QByteArray &a, QByteArray *b, bool oaep) const +{ + if(v_key.isNull()) + return false; + return v_key.decrypt(a, b, oaep); +} + +RSAKey RSA::generateKey(unsigned int bits) +{ + RSAKey k; + k.generate(bits); + return k; +} + + +//---------------------------------------------------------------------------- +// Cert +//---------------------------------------------------------------------------- +class Cert::Private +{ +public: + Private() + { + c = 0; + } + + ~Private() + { + delete c; + } + + QCA_CertContext *c; +}; + +Cert::Cert() +{ + d = new Private; + // crash because this is returning 0 + d->c = (QCA_CertContext *)getContext(CAP_X509); +} + +Cert::Cert(const Cert &from) +{ + d = new Private; + *this = from; +} + +Cert & Cert::operator=(const Cert &from) +{ + delete d->c; + if ( from.d->c ) + d->c = from.d->c->clone(); + else + d->c = 0; + return *this; +} + +Cert::~Cert() +{ + delete d; +} + +void Cert::fromContext(QCA_CertContext *ctx) +{ + delete d->c; + d->c = ctx; +} + +bool Cert::isNull() const +{ + return d->c->isNull(); +} + +QString Cert::commonName() const +{ + CertProperties props = subject(); + return props["CN"]; +} + +QString Cert::serialNumber() const +{ + return d->c->serialNumber(); +} + +QString Cert::subjectString() const +{ + return d->c->subjectString(); +} + +QString Cert::issuerString() const +{ + return d->c->issuerString(); +} + +CertProperties Cert::subject() const +{ + QValueList<QCA_CertProperty> list = d->c->subject(); + CertProperties props; + for(QValueList<QCA_CertProperty>::ConstIterator it = list.begin(); it != list.end(); ++it) + props[(*it).var] = (*it).val; + return props; +} + +CertProperties Cert::issuer() const +{ + QValueList<QCA_CertProperty> list = d->c->issuer(); + CertProperties props; + for(QValueList<QCA_CertProperty>::ConstIterator it = list.begin(); it != list.end(); ++it) + props[(*it).var] = (*it).val; + return props; +} + +QDateTime Cert::notBefore() const +{ + return d->c->notBefore(); +} + +QDateTime Cert::notAfter() const +{ + return d->c->notAfter(); +} + +QByteArray Cert::toDER() const +{ + QByteArray out; + if(!d->c->toDER(&out)) + return QByteArray(); + return out; +} + +bool Cert::fromDER(const QByteArray &a) +{ + return d->c->createFromDER(a.data(), a.size()); +} + +QString Cert::toPEM() const +{ + QByteArray out; + if(!d->c->toPEM(&out)) + return QByteArray(); + + QCString cs; + cs.resize(out.size()+1); + memcpy(cs.data(), out.data(), out.size()); + return QString::fromLatin1(cs); +} + +bool Cert::fromPEM(const QString &str) +{ + QCString cs = str.latin1(); + QByteArray a(cs.length()); + memcpy(a.data(), cs.data(), a.size()); + return d->c->createFromPEM(a.data(), a.size()); +} + + +//---------------------------------------------------------------------------- +// TLS +//---------------------------------------------------------------------------- +class TLS::Private +{ +public: + Private() + { + c = (QCA_TLSContext *)getContext(CAP_TLS); + } + + ~Private() + { + delete c; + } + + void reset() + { + handshaken = false; + closing = false; + in.resize(0); + out.resize(0); + from_net.resize(0); + to_net.resize(0); + host = ""; + hostMismatch = false; + // this causes the crash, because the Cert ctor is setting a null context + cert = Cert(); + bytesEncoded = 0; + tryMore = false; + } + + void appendArray(QByteArray *a, const QByteArray &b) + { + int oldsize = a->size(); + a->resize(oldsize + b.size()); + memcpy(a->data() + oldsize, b.data(), b.size()); + } + + Cert cert; + QCA_TLSContext *c; + QByteArray in, out, to_net, from_net; + int bytesEncoded; + bool tryMore; + bool handshaken; + QString host; + bool hostMismatch; + bool closing; + + Cert ourCert; + RSAKey ourKey; + QPtrList<QCA_CertContext> store; +}; + +TLS::TLS(QObject *parent) +:QObject(parent) +{ + d = new Private; +} + +TLS::~TLS() +{ + delete d; +} + +void TLS::setCertificate(const Cert &cert, const RSAKey &key) +{ + d->ourCert = cert; + d->ourKey = key; +} + +void TLS::setCertificateStore(const QPtrList<Cert> &store) +{ + // convert the cert list into a context list + d->store.clear(); + QPtrListIterator<Cert> it(store); + for(Cert *cert; (cert = it.current()); ++it) + d->store.append(cert->d->c); +} + +void TLS::reset() +{ + d->reset(); +} + +bool TLS::startClient(const QString &host) +{ + d->reset(); + d->host = host; + + if(!d->c->startClient(d->store, *d->ourCert.d->c, *d->ourKey.d->c)) + return false; + QTimer::singleShot(0, this, SLOT(update())); + return true; +} + +bool TLS::startServer() +{ + d->reset(); + + if(!d->c->startServer(d->store, *d->ourCert.d->c, *d->ourKey.d->c)) + return false; + QTimer::singleShot(0, this, SLOT(update())); + return true; +} + +void TLS::close() +{ + if(!d->handshaken || d->closing) + return; + + d->closing = true; + QTimer::singleShot(0, this, SLOT(update())); +} + +bool TLS::isHandshaken() const +{ + return d->handshaken; +} + +void TLS::write(const QByteArray &a) +{ + d->appendArray(&d->out, a); + update(); +} + +QByteArray TLS::read() +{ + QByteArray a = d->in.copy(); + d->in.resize(0); + return a; +} + +void TLS::writeIncoming(const QByteArray &a) +{ + d->appendArray(&d->from_net, a); + update(); +} + +QByteArray TLS::readOutgoing() +{ + QByteArray a = d->to_net.copy(); + d->to_net.resize(0); + return a; +} + +QByteArray TLS::readUnprocessed() +{ + QByteArray a = d->from_net.copy(); + d->from_net.resize(0); + return a; +} + +const Cert & TLS::peerCertificate() const +{ + return d->cert; +} + +int TLS::certificateValidityResult() const +{ + if(d->hostMismatch) + return QCA::TLS::HostMismatch; + else + return d->c->validityResult(); +} + +void TLS::update() +{ + bool force_read = false; + bool eof = false; + bool done = false; + QGuardedPtr<TLS> self = this; + + if(d->closing) { + QByteArray a; + int r = d->c->shutdown(d->from_net, &a); + d->from_net.resize(0); + if(r == QCA_TLSContext::Error) { + reset(); + error(ErrHandshake); + return; + } + if(r == QCA_TLSContext::Success) { + d->from_net = d->c->unprocessed().copy(); + done = true; + } + d->appendArray(&d->to_net, a); + } + else { + if(!d->handshaken) { + QByteArray a; + int r = d->c->handshake(d->from_net, &a); + d->from_net.resize(0); + if(r == QCA_TLSContext::Error) { + reset(); + error(ErrHandshake); + return; + } + d->appendArray(&d->to_net, a); + if(r == QCA_TLSContext::Success) { + QCA_CertContext *cc = d->c->peerCertificate(); + if(cc && !d->host.isEmpty() && d->c->validityResult() == QCA::TLS::Valid) { + if(!cc->matchesAddress(d->host)) + d->hostMismatch = true; + } + d->cert.fromContext(cc); + d->handshaken = true; + handshaken(); + if(!self) + return; + + // there is a teeny tiny possibility that incoming data awaits. let us get it. + force_read = true; + } + } + + if(d->handshaken) { + if(!d->out.isEmpty() || d->tryMore) { + d->tryMore = false; + QByteArray a; + int enc; + bool more = false; + bool ok = d->c->encode(d->out, &a, &enc); + eof = d->c->eof(); + if(ok && enc < (int)d->out.size()) + more = true; + d->out.resize(0); + if(!eof) { + if(!ok) { + reset(); + error(ErrCrypt); + return; + } + d->bytesEncoded += enc; + if(more) + d->tryMore = true; + d->appendArray(&d->to_net, a); + } + } + if(!d->from_net.isEmpty() || force_read) { + QByteArray a, b; + bool ok = d->c->decode(d->from_net, &a, &b); + eof = d->c->eof(); + d->from_net.resize(0); + if(!ok) { + reset(); + error(ErrCrypt); + return; + } + d->appendArray(&d->in, a); + d->appendArray(&d->to_net, b); + } + + if(!d->in.isEmpty()) { + readyRead(); + if(!self) + return; + } + } + } + + if(!d->to_net.isEmpty()) { + int bytes = d->bytesEncoded; + d->bytesEncoded = 0; + readyReadOutgoing(bytes); + if(!self) + return; + } + + if(eof) { + close(); + if(!self) + return; + return; + } + + if(d->closing && done) { + reset(); + closed(); + } +} + + +//---------------------------------------------------------------------------- +// SASL +//---------------------------------------------------------------------------- +QString saslappname = "qca"; +class SASL::Private +{ +public: + Private() + { + c = (QCA_SASLContext *)getContext(CAP_SASL); + } + + ~Private() + { + delete c; + } + + void setSecurityProps() + { + c->setSecurityProps(noPlain, noActive, noDict, noAnon, reqForward, reqCreds, reqMutual, ssfmin, ssfmax, ext_authid, ext_ssf); + } + + // security opts + bool noPlain, noActive, noDict, noAnon, reqForward, reqCreds, reqMutual; + int ssfmin, ssfmax; + QString ext_authid; + int ext_ssf; + + bool tried; + QCA_SASLContext *c; + QHostAddress localAddr, remoteAddr; + int localPort, remotePort; + QByteArray stepData; + bool allowCSF; + bool first, server; + + QByteArray inbuf, outbuf; +}; + +SASL::SASL(QObject *parent) +:QObject(parent) +{ + d = new Private; + reset(); +} + +SASL::~SASL() +{ + delete d; +} + +void SASL::setAppName(const QString &name) +{ + saslappname = name; +} + +void SASL::reset() +{ + d->localPort = -1; + d->remotePort = -1; + + d->noPlain = false; + d->noActive = false; + d->noDict = false; + d->noAnon = false; + d->reqForward = false; + d->reqCreds = false; + d->reqMutual = false; + d->ssfmin = 0; + d->ssfmax = 0; + d->ext_authid = ""; + d->ext_ssf = 0; + + d->inbuf.resize(0); + d->outbuf.resize(0); + + d->c->reset(); +} + +int SASL::errorCondition() const +{ + return d->c->errorCond(); +} + +void SASL::setAllowPlain(bool b) +{ + d->noPlain = !b; +} + +void SASL::setAllowAnonymous(bool b) +{ + d->noAnon = !b; +} + +void SASL::setAllowActiveVulnerable(bool b) +{ + d->noActive = !b; +} + +void SASL::setAllowDictionaryVulnerable(bool b) +{ + d->noDict = !b; +} + +void SASL::setRequireForwardSecrecy(bool b) +{ + d->reqForward = b; +} + +void SASL::setRequirePassCredentials(bool b) +{ + d->reqCreds = b; +} + +void SASL::setRequireMutualAuth(bool b) +{ + d->reqMutual = b; +} + +void SASL::setMinimumSSF(int x) +{ + d->ssfmin = x; +} + +void SASL::setMaximumSSF(int x) +{ + d->ssfmax = x; +} + +void SASL::setExternalAuthID(const QString &authid) +{ + d->ext_authid = authid; +} + +void SASL::setExternalSSF(int x) +{ + d->ext_ssf = x; +} + +void SASL::setLocalAddr(const QHostAddress &addr, Q_UINT16 port) +{ + d->localAddr = addr; + d->localPort = port; +} + +void SASL::setRemoteAddr(const QHostAddress &addr, Q_UINT16 port) +{ + d->remoteAddr = addr; + d->remotePort = port; +} + +bool SASL::startClient(const QString &service, const QString &host, const QStringList &mechlist, bool allowClientSendFirst) +{ + QCA_SASLHostPort la, ra; + if(d->localPort != -1) { + la.addr = d->localAddr; + la.port = d->localPort; + } + if(d->remotePort != -1) { + ra.addr = d->remoteAddr; + ra.port = d->remotePort; + } + + d->allowCSF = allowClientSendFirst; + d->c->setCoreProps(service, host, d->localPort != -1 ? &la : 0, d->remotePort != -1 ? &ra : 0); + d->setSecurityProps(); + + if(!d->c->clientStart(mechlist)) + return false; + d->first = true; + d->server = false; + d->tried = false; + QTimer::singleShot(0, this, SLOT(tryAgain())); + return true; +} + +bool SASL::startServer(const QString &service, const QString &host, const QString &realm, QStringList *mechlist) +{ + QCA_SASLHostPort la, ra; + if(d->localPort != -1) { + la.addr = d->localAddr; + la.port = d->localPort; + } + if(d->remotePort != -1) { + ra.addr = d->remoteAddr; + ra.port = d->remotePort; + } + + d->c->setCoreProps(service, host, d->localPort != -1 ? &la : 0, d->remotePort != -1 ? &ra : 0); + d->setSecurityProps(); + + if(!d->c->serverStart(realm, mechlist, saslappname)) + return false; + d->first = true; + d->server = true; + d->tried = false; + return true; +} + +void SASL::putServerFirstStep(const QString &mech) +{ + int r = d->c->serverFirstStep(mech, 0); + handleServerFirstStep(r); +} + +void SASL::putServerFirstStep(const QString &mech, const QByteArray &clientInit) +{ + int r = d->c->serverFirstStep(mech, &clientInit); + handleServerFirstStep(r); +} + +void SASL::handleServerFirstStep(int r) +{ + if(r == QCA_SASLContext::Success) + authenticated(); + else if(r == QCA_SASLContext::Continue) + nextStep(d->c->result()); + else if(r == QCA_SASLContext::AuthCheck) + tryAgain(); + else + error(ErrAuth); +} + +void SASL::putStep(const QByteArray &stepData) +{ + d->stepData = stepData.copy(); + tryAgain(); +} + +void SASL::setUsername(const QString &user) +{ + d->c->setClientParams(&user, 0, 0, 0); +} + +void SASL::setAuthzid(const QString &authzid) +{ + d->c->setClientParams(0, &authzid, 0, 0); +} + +void SASL::setPassword(const QString &pass) +{ + d->c->setClientParams(0, 0, &pass, 0); +} + +void SASL::setRealm(const QString &realm) +{ + d->c->setClientParams(0, 0, 0, &realm); +} + +void SASL::continueAfterParams() +{ + tryAgain(); +} + +void SASL::continueAfterAuthCheck() +{ + tryAgain(); +} + +void SASL::tryAgain() +{ + int r; + + if(d->server) { + if(!d->tried) { + r = d->c->nextStep(d->stepData); + d->tried = true; + } + else { + r = d->c->tryAgain(); + } + + if(r == QCA_SASLContext::Error) { + error(ErrAuth); + return; + } + else if(r == QCA_SASLContext::Continue) { + d->tried = false; + nextStep(d->c->result()); + return; + } + else if(r == QCA_SASLContext::AuthCheck) { + authCheck(d->c->username(), d->c->authzid()); + return; + } + } + else { + if(d->first) { + if(!d->tried) { + r = d->c->clientFirstStep(d->allowCSF); + d->tried = true; + } + else + r = d->c->tryAgain(); + + if(r == QCA_SASLContext::Error) { + error(ErrAuth); + return; + } + else if(r == QCA_SASLContext::NeedParams) { + //d->tried = false; + QCA_SASLNeedParams np = d->c->clientParamsNeeded(); + needParams(np.user, np.authzid, np.pass, np.realm); + return; + } + + QString mech = d->c->mech(); + const QByteArray *clientInit = d->c->clientInit(); + + d->first = false; + d->tried = false; + clientFirstStep(mech, clientInit); + } + else { + if(!d->tried) { + r = d->c->nextStep(d->stepData); + d->tried = true; + } + else + r = d->c->tryAgain(); + + if(r == QCA_SASLContext::Error) { + error(ErrAuth); + return; + } + else if(r == QCA_SASLContext::NeedParams) { + //d->tried = false; + QCA_SASLNeedParams np = d->c->clientParamsNeeded(); + needParams(np.user, np.authzid, np.pass, np.realm); + return; + } + d->tried = false; + //else if(r == QCA_SASLContext::Continue) { + nextStep(d->c->result()); + // return; + //} + } + } + + if(r == QCA_SASLContext::Success) + authenticated(); + else if(r == QCA_SASLContext::Error) + error(ErrAuth); +} + +int SASL::ssf() const +{ + return d->c->security(); +} + +void SASL::write(const QByteArray &a) +{ + QByteArray b; + if(!d->c->encode(a, &b)) { + error(ErrCrypt); + return; + } + int oldsize = d->outbuf.size(); + d->outbuf.resize(oldsize + b.size()); + memcpy(d->outbuf.data() + oldsize, b.data(), b.size()); + readyReadOutgoing(a.size()); +} + +QByteArray SASL::read() +{ + QByteArray a = d->inbuf.copy(); + d->inbuf.resize(0); + return a; +} + +void SASL::writeIncoming(const QByteArray &a) +{ + QByteArray b; + if(!d->c->decode(a, &b)) { + error(ErrCrypt); + return; + } + int oldsize = d->inbuf.size(); + d->inbuf.resize(oldsize + b.size()); + memcpy(d->inbuf.data() + oldsize, b.data(), b.size()); + readyRead(); +} + +QByteArray SASL::readOutgoing() +{ + QByteArray a = d->outbuf.copy(); + d->outbuf.resize(0); + return a; +} + +#include "qca.moc" diff --git a/kopete/protocols/groupwise/libgroupwise/qca/src/qca.h b/kopete/protocols/groupwise/libgroupwise/qca/src/qca.h new file mode 100644 index 00000000..e7cd1609 --- /dev/null +++ b/kopete/protocols/groupwise/libgroupwise/qca/src/qca.h @@ -0,0 +1,466 @@ +/* + * qca.h - Qt Cryptographic Architecture + * Copyright (C) 2003 Justin Karneges + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef QCA_H +#define QCA_H + +#include<qstring.h> +#include<qcstring.h> +#include<qdatetime.h> +#include<qmap.h> +#include<qptrlist.h> +#include<qobject.h> + +#ifdef Q_OS_WIN32 +# ifndef QCA_STATIC +# ifdef QCA_MAKEDLL +# define QCA_EXPORT __declspec(dllexport) +# else +# define QCA_EXPORT __declspec(dllimport) +# endif +# endif +#endif +#ifndef QCA_EXPORT +#define QCA_EXPORT +#endif + +#ifdef Q_OS_WIN32 +# ifdef QCA_PLUGIN_DLL +# define QCA_PLUGIN_EXPORT extern "C" __declspec(dllexport) +# else +# define QCA_PLUGIN_EXPORT extern "C" __declspec(dllimport) +# endif +#endif +#ifndef QCA_PLUGIN_EXPORT +#define QCA_PLUGIN_EXPORT extern "C" +#endif + +class QHostAddress; +class QStringList; + +class QCAProvider; +class QCA_HashContext; +class QCA_CipherContext; +class QCA_CertContext; + +namespace QCA +{ + enum { + CAP_SHA1 = 0x0001, + CAP_SHA256 = 0x0002, + CAP_MD5 = 0x0004, + CAP_BlowFish = 0x0008, + CAP_TripleDES = 0x0010, + CAP_AES128 = 0x0020, + CAP_AES256 = 0x0040, + CAP_RSA = 0x0080, + CAP_X509 = 0x0100, + CAP_TLS = 0x0200, + CAP_SASL = 0x0400 + }; + + enum { + CBC = 0x0001, + CFB = 0x0002 + }; + + enum { + Encrypt = 0x0001, + Decrypt = 0x0002 + }; + + QCA_EXPORT void init(); + QCA_EXPORT bool isSupported(int capabilities); + QCA_EXPORT void insertProvider(QCAProvider *); + QCA_EXPORT void unloadAllPlugins(); + + QCA_EXPORT QString arrayToHex(const QByteArray &); + QCA_EXPORT QByteArray hexToArray(const QString &); + + class QCA_EXPORT Hash + { + public: + Hash(const Hash &); + Hash & operator=(const Hash &); + ~Hash(); + + void clear(); + void update(const QByteArray &a); + QByteArray final(); + + protected: + Hash(QCA_HashContext *); + + private: + class Private; + Private *d; + }; + + template <class T> + class QCA_EXPORT HashStatic + { + public: + HashStatic<T>() {} + + static QByteArray hash(const QByteArray &a) + { + T obj; + obj.update(a); + return obj.final(); + } + + static QByteArray hash(const QCString &cs) + { + QByteArray a(cs.length()); + memcpy(a.data(), cs.data(), a.size()); + return hash(a); + } + + static QString hashToString(const QByteArray &a) + { + return arrayToHex(hash(a)); + } + + static QString hashToString(const QCString &cs) + { + return arrayToHex(hash(cs)); + } + }; + + class QCA_EXPORT Cipher + { + public: + Cipher(const Cipher &); + Cipher & operator=(const Cipher &); + ~Cipher(); + + QByteArray dyn_generateKey(int size=-1) const; + QByteArray dyn_generateIV() const; + void reset(int dir, int mode, const QByteArray &key, const QByteArray &iv, bool pad=true); + bool update(const QByteArray &a); + QByteArray final(bool *ok=0); + + protected: + Cipher(QCA_CipherContext *, int dir, int mode, const QByteArray &key, const QByteArray &iv, bool pad); + + private: + class Private; + Private *d; + }; + + template <class T> + class QCA_EXPORT CipherStatic + { + public: + CipherStatic<T>() {} + + static QByteArray generateKey(int size=-1) + { + T obj; + return obj.dyn_generateKey(size); + } + + static QByteArray generateIV() + { + T obj; + return obj.dyn_generateIV(); + } + }; + + class QCA_EXPORT SHA1 : public Hash, public HashStatic<SHA1> + { + public: + SHA1(); + }; + + class QCA_EXPORT SHA256 : public Hash, public HashStatic<SHA256> + { + public: + SHA256(); + }; + + class QCA_EXPORT MD5 : public Hash, public HashStatic<MD5> + { + public: + MD5(); + }; + + class QCA_EXPORT BlowFish : public Cipher, public CipherStatic<BlowFish> + { + public: + BlowFish(int dir=Encrypt, int mode=CBC, const QByteArray &key=QByteArray(), const QByteArray &iv=QByteArray(), bool pad=true); + }; + + class QCA_EXPORT TripleDES : public Cipher, public CipherStatic<TripleDES> + { + public: + TripleDES(int dir=Encrypt, int mode=CBC, const QByteArray &key=QByteArray(), const QByteArray &iv=QByteArray(), bool pad=true); + }; + + class QCA_EXPORT AES128 : public Cipher, public CipherStatic<AES128> + { + public: + AES128(int dir=Encrypt, int mode=CBC, const QByteArray &key=QByteArray(), const QByteArray &iv=QByteArray(), bool pad=true); + }; + + class QCA_EXPORT AES256 : public Cipher, public CipherStatic<AES256> + { + public: + AES256(int dir=Encrypt, int mode=CBC, const QByteArray &key=QByteArray(), const QByteArray &iv=QByteArray(), bool pad=true); + }; + + class RSA; + class QCA_EXPORT RSAKey + { + public: + RSAKey(); + RSAKey(const RSAKey &from); + RSAKey & operator=(const RSAKey &from); + ~RSAKey(); + + bool isNull() const; + bool havePublic() const; + bool havePrivate() const; + + QByteArray toDER(bool publicOnly=false) const; + bool fromDER(const QByteArray &a); + + QString toPEM(bool publicOnly=false) const; + bool fromPEM(const QString &); + + // only call if you know what you are doing + bool fromNative(void *); + + private: + class Private; + Private *d; + + friend class RSA; + friend class TLS; + bool encrypt(const QByteArray &a, QByteArray *out, bool oaep) const; + bool decrypt(const QByteArray &a, QByteArray *out, bool oaep) const; + bool generate(unsigned int bits); + }; + + class QCA_EXPORT RSA + { + public: + RSA(); + ~RSA(); + + RSAKey key() const; + void setKey(const RSAKey &); + + bool encrypt(const QByteArray &a, QByteArray *out, bool oaep=false) const; + bool decrypt(const QByteArray &a, QByteArray *out, bool oaep=false) const; + + static RSAKey generateKey(unsigned int bits); + + private: + RSAKey v_key; + }; + + typedef QMap<QString, QString> CertProperties; + class QCA_EXPORT Cert + { + public: + Cert(); + Cert(const Cert &); + Cert & operator=(const Cert &); + ~Cert(); + + bool isNull() const; + + QString commonName() const; + QString serialNumber() const; + QString subjectString() const; + QString issuerString() const; + CertProperties subject() const; + CertProperties issuer() const; + QDateTime notBefore() const; + QDateTime notAfter() const; + + QByteArray toDER() const; + bool fromDER(const QByteArray &a); + + QString toPEM() const; + bool fromPEM(const QString &); + + private: + class Private; + Private *d; + + friend class TLS; + void fromContext(QCA_CertContext *); + }; + + class QCA_EXPORT TLS : public QObject + { + Q_OBJECT + public: + enum Validity { + NoCert, + Valid, + HostMismatch, + Rejected, + Untrusted, + SignatureFailed, + InvalidCA, + InvalidPurpose, + SelfSigned, + Revoked, + PathLengthExceeded, + Expired, + Unknown + }; + enum Error { ErrHandshake, ErrCrypt }; + + TLS(QObject *parent=0); + ~TLS(); + + void setCertificate(const Cert &cert, const RSAKey &key); + void setCertificateStore(const QPtrList<Cert> &store); // note: store must persist + + void reset(); + bool startClient(const QString &host=""); + bool startServer(); + void close(); + bool isHandshaken() const; + + // plain (application side) + void write(const QByteArray &a); + QByteArray read(); + + // encoded (socket side) + void writeIncoming(const QByteArray &a); + QByteArray readOutgoing(); + QByteArray readUnprocessed(); + + // cert related + const Cert & peerCertificate() const; + int certificateValidityResult() const; + + signals: + void handshaken(); + void readyRead(); + void readyReadOutgoing(int plainBytes); + void closed(); + void error(int); + + private slots: + void update(); + + private: + class Private; + Private *d; + }; + + class QCA_EXPORT SASL : public QObject + { + Q_OBJECT + public: + enum Error { ErrAuth, ErrCrypt }; + enum ErrorCond { + NoMech, + BadProto, + BadServ, + BadAuth, + NoAuthzid, + TooWeak, + NeedEncrypt, + Expired, + Disabled, + NoUser, + RemoteUnavail + }; + SASL(QObject *parent=0); + ~SASL(); + + static void setAppName(const QString &name); + + void reset(); + int errorCondition() const; + + // options + void setAllowPlain(bool); + void setAllowAnonymous(bool); + void setAllowActiveVulnerable(bool); + void setAllowDictionaryVulnerable(bool); + void setRequireForwardSecrecy(bool); + void setRequirePassCredentials(bool); + void setRequireMutualAuth(bool); + + void setMinimumSSF(int); + void setMaximumSSF(int); + void setExternalAuthID(const QString &authid); + void setExternalSSF(int); + + void setLocalAddr(const QHostAddress &addr, Q_UINT16 port); + void setRemoteAddr(const QHostAddress &addr, Q_UINT16 port); + + // initialize + bool startClient(const QString &service, const QString &host, const QStringList &mechlist, bool allowClientSendFirst=true); + bool startServer(const QString &service, const QString &host, const QString &realm, QStringList *mechlist); + + // authentication + void putStep(const QByteArray &stepData); + void putServerFirstStep(const QString &mech); + void putServerFirstStep(const QString &mech, const QByteArray &clientInit); + void setUsername(const QString &user); + void setAuthzid(const QString &auth); + void setPassword(const QString &pass); + void setRealm(const QString &realm); + void continueAfterParams(); + void continueAfterAuthCheck(); + + // security layer + int ssf() const; + void write(const QByteArray &a); + QByteArray read(); + void writeIncoming(const QByteArray &a); + QByteArray readOutgoing(); + + signals: + // for authentication + void clientFirstStep(const QString &mech, const QByteArray *clientInit); + void nextStep(const QByteArray &stepData); + void needParams(bool user, bool authzid, bool pass, bool realm); + void authCheck(const QString &user, const QString &authzid); + void authenticated(); + + // for security layer + void readyRead(); + void readyReadOutgoing(int plainBytes); + + // error + void error(int); + + private slots: + void tryAgain(); + + private: + class Private; + Private *d; + + void handleServerFirstStep(int r); + }; +} + +#endif diff --git a/kopete/protocols/groupwise/libgroupwise/qca/src/qcaprovider.h b/kopete/protocols/groupwise/libgroupwise/qca/src/qcaprovider.h new file mode 100644 index 00000000..a7f1805b --- /dev/null +++ b/kopete/protocols/groupwise/libgroupwise/qca/src/qcaprovider.h @@ -0,0 +1,191 @@ +/* + * qcaprovider.h - QCA Plugin API + * Copyright (C) 2003 Justin Karneges + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef QCAPROVIDER_H +#define QCAPROVIDER_H + +#include<qglobal.h> +#include<qstring.h> +#include<qdatetime.h> +#include<qobject.h> +#include<qhostaddress.h> +#include"qca.h" + +#define QCA_PLUGIN_VERSION 1 + +class QCAProvider +{ +public: + QCAProvider() {} + virtual ~QCAProvider() {} + + virtual void init()=0; + virtual int qcaVersion() const=0; + virtual int capabilities() const=0; + virtual void *context(int cap)=0; +}; + +class QCA_HashContext +{ +public: + virtual ~QCA_HashContext() {} + + virtual QCA_HashContext *clone()=0; + virtual void reset()=0; + virtual void update(const char *in, unsigned int len)=0; + virtual void final(QByteArray *out)=0; +}; + +class QCA_CipherContext +{ +public: + virtual ~QCA_CipherContext() {} + + virtual QCA_CipherContext *clone()=0; + virtual int keySize()=0; + virtual int blockSize()=0; + virtual bool generateKey(char *out, int keysize=-1)=0; + virtual bool generateIV(char *out)=0; + + virtual bool setup(int dir, int mode, const char *key, int keysize, const char *iv, bool pad)=0; + virtual bool update(const char *in, unsigned int len)=0; + virtual bool final(QByteArray *out)=0; +}; + +class QCA_RSAKeyContext +{ +public: + virtual ~QCA_RSAKeyContext() {} + + virtual QCA_RSAKeyContext *clone() const=0; + virtual bool isNull() const=0; + virtual bool havePublic() const=0; + virtual bool havePrivate() const=0; + virtual bool createFromDER(const char *in, unsigned int len)=0; + virtual bool createFromPEM(const char *in, unsigned int len)=0; + virtual bool createFromNative(void *in)=0; + virtual bool generate(unsigned int bits)=0; + virtual bool toDER(QByteArray *out, bool publicOnly)=0; + virtual bool toPEM(QByteArray *out, bool publicOnly)=0; + + virtual bool encrypt(const QByteArray &in, QByteArray *out, bool oaep)=0; + virtual bool decrypt(const QByteArray &in, QByteArray *out, bool oaep)=0; +}; + +struct QCA_CertProperty +{ + QString var; + QString val; +}; + +class QCA_CertContext +{ +public: + virtual ~QCA_CertContext() {} + + virtual QCA_CertContext *clone() const=0; + virtual bool isNull() const=0; + virtual bool createFromDER(const char *in, unsigned int len)=0; + virtual bool createFromPEM(const char *in, unsigned int len)=0; + virtual bool toDER(QByteArray *out)=0; + virtual bool toPEM(QByteArray *out)=0; + + virtual QString serialNumber() const=0; + virtual QString subjectString() const=0; + virtual QString issuerString() const=0; + virtual QValueList<QCA_CertProperty> subject() const=0; + virtual QValueList<QCA_CertProperty> issuer() const=0; + virtual QDateTime notBefore() const=0; + virtual QDateTime notAfter() const=0; + virtual bool matchesAddress(const QString &realHost) const=0; +}; + +class QCA_TLSContext +{ +public: + enum Result { Success, Error, Continue }; + virtual ~QCA_TLSContext() {} + + virtual void reset()=0; + virtual bool startClient(const QPtrList<QCA_CertContext> &store, const QCA_CertContext &cert, const QCA_RSAKeyContext &key)=0; + virtual bool startServer(const QPtrList<QCA_CertContext> &store, const QCA_CertContext &cert, const QCA_RSAKeyContext &key)=0; + + virtual int handshake(const QByteArray &in, QByteArray *out)=0; + virtual int shutdown(const QByteArray &in, QByteArray *out)=0; + virtual bool encode(const QByteArray &plain, QByteArray *to_net, int *encoded)=0; + virtual bool decode(const QByteArray &from_net, QByteArray *plain, QByteArray *to_net)=0; + virtual bool eof() const=0; + virtual QByteArray unprocessed()=0; + + virtual QCA_CertContext *peerCertificate() const=0; + virtual int validityResult() const=0; +}; + +struct QCA_SASLHostPort +{ + QHostAddress addr; + Q_UINT16 port; +}; + +struct QCA_SASLNeedParams +{ + bool user, authzid, pass, realm; +}; + +class QCA_SASLContext +{ +public: + enum Result { Success, Error, NeedParams, AuthCheck, Continue }; + virtual ~QCA_SASLContext() {} + + // common + virtual void reset()=0; + virtual void setCoreProps(const QString &service, const QString &host, QCA_SASLHostPort *local, QCA_SASLHostPort *remote)=0; + virtual void setSecurityProps(bool noPlain, bool noActive, bool noDict, bool noAnon, bool reqForward, bool reqCreds, bool reqMutual, int ssfMin, int ssfMax, const QString &_ext_authid, int _ext_ssf)=0; + virtual int security() const=0; + virtual int errorCond() const=0; + + // init / first step + virtual bool clientStart(const QStringList &mechlist)=0; + virtual int clientFirstStep(bool allowClientSendFirst)=0; + virtual bool serverStart(const QString &realm, QStringList *mechlist, const QString &name)=0; + virtual int serverFirstStep(const QString &mech, const QByteArray *in)=0; + + // get / set params + virtual QCA_SASLNeedParams clientParamsNeeded() const=0; + virtual void setClientParams(const QString *user, const QString *authzid, const QString *pass, const QString *realm)=0; + virtual QString username() const=0; + virtual QString authzid() const=0; + + // continue steps + virtual int nextStep(const QByteArray &in)=0; + virtual int tryAgain()=0; + + // results + virtual QString mech() const=0; + virtual const QByteArray *clientInit() const=0; + virtual QByteArray result() const=0; + + // security layer + virtual bool encode(const QByteArray &in, QByteArray *out)=0; + virtual bool decode(const QByteArray &in, QByteArray *out)=0; +}; + +#endif diff --git a/kopete/protocols/groupwise/libgroupwise/qcatlshandler.cpp b/kopete/protocols/groupwise/libgroupwise/qcatlshandler.cpp new file mode 100644 index 00000000..366f2afa --- /dev/null +++ b/kopete/protocols/groupwise/libgroupwise/qcatlshandler.cpp @@ -0,0 +1,122 @@ +/* + qcatlshandler.cpp - Kopete Groupwise Protocol + + Copyright (c) 2004 SUSE Linux AG http://www.suse.com + + Based on Iris, Copyright (C) 2003 Justin Karneges + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#include <qtimer.h> + +#include "qca.h" + +#include "qcatlshandler.h" + +class QCATLSHandler::Private +{ +public: + QCA::TLS *tls; + int state, err; +}; + +QCATLSHandler::QCATLSHandler(QCA::TLS *parent) +:TLSHandler(parent) +{ + d = new Private; + d->tls = parent; + connect(d->tls, SIGNAL(handshaken()), SLOT(tls_handshaken())); + connect(d->tls, SIGNAL(readyRead()), SLOT(tls_readyRead())); + connect(d->tls, SIGNAL(readyReadOutgoing(int)), SLOT(tls_readyReadOutgoing(int))); + connect(d->tls, SIGNAL(closed()), SLOT(tls_closed())); + connect(d->tls, SIGNAL(error(int)), SLOT(tls_error(int))); + d->state = 0; + d->err = -1; +} + +QCATLSHandler::~QCATLSHandler() +{ + delete d; +} + +QCA::TLS *QCATLSHandler::tls() const +{ + return d->tls; +} + +int QCATLSHandler::tlsError() const +{ + return d->err; +} + +void QCATLSHandler::reset() +{ + d->tls->reset(); + d->state = 0; +} + +void QCATLSHandler::startClient(const QString &host) +{ + d->state = 0; + d->err = -1; + if(!d->tls->startClient(host)) + QTimer::singleShot(0, this, SIGNAL(fail())); +} + +void QCATLSHandler::write(const QByteArray &a) +{ + d->tls->write(a); +} + +void QCATLSHandler::writeIncoming(const QByteArray &a) +{ + d->tls->writeIncoming(a); +} + +void QCATLSHandler::continueAfterHandshake() +{ + if(d->state == 2) { + success(); + d->state = 3; + } +} + +void QCATLSHandler::tls_handshaken() +{ + d->state = 2; + tlsHandshaken(); +} + +void QCATLSHandler::tls_readyRead() +{ + readyRead(d->tls->read()); +} + +void QCATLSHandler::tls_readyReadOutgoing(int plainBytes) +{ + readyReadOutgoing(d->tls->readOutgoing(), plainBytes); +} + +void QCATLSHandler::tls_closed() +{ + closed(); +} + +void QCATLSHandler::tls_error(int x) +{ + d->err = x; + d->state = 0; + fail(); +} + +#include "qcatlshandler.moc" diff --git a/kopete/protocols/groupwise/libgroupwise/qcatlshandler.h b/kopete/protocols/groupwise/libgroupwise/qcatlshandler.h new file mode 100644 index 00000000..a550d54b --- /dev/null +++ b/kopete/protocols/groupwise/libgroupwise/qcatlshandler.h @@ -0,0 +1,61 @@ +/* + qcatlshandler.h - Kopete Groupwise Protocol + + Copyright (c) 2004 SUSE Linux AG http://www.suse.com + + Based on Iris, Copyright (C) 2003 Justin Karneges + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#ifndef GWQCATLSHANDLER_H +#define GWQCATLSHANDLER_H + +//#include <qtimer.h> +#include "tlshandler.h" + +class QCA::TLS; + +class QCATLSHandler : public TLSHandler +{ + Q_OBJECT +public: + QCATLSHandler(QCA::TLS *parent); + ~QCATLSHandler(); + + QCA::TLS *tls() const; + int tlsError() const; + + void reset(); + void startClient(const QString &host); + void write(const QByteArray &a); + void writeIncoming(const QByteArray &a); + +signals: + void tlsHandshaken(); + +public slots: + void continueAfterHandshake(); + +private slots: + void tls_handshaken(); + void tls_readyRead(); + void tls_readyReadOutgoing(int); + void tls_closed(); + void tls_error(int); + +private: + class Private; + Private *d; +}; + +#endif diff --git a/kopete/protocols/groupwise/libgroupwise/request.cpp b/kopete/protocols/groupwise/libgroupwise/request.cpp new file mode 100644 index 00000000..508bf6a0 --- /dev/null +++ b/kopete/protocols/groupwise/libgroupwise/request.cpp @@ -0,0 +1,34 @@ +/* + request.cpp - Kopete Groupwise Protocol + + Copyright (c) 2004 SUSE Linux AG http://www.suse.com + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#include "request.h" + +Request::Request( const int transactionId, const QString &command ) +: UserTransfer(transactionId), m_command( command ) +{ +} + +Request::~Request() +{ +} + +QString Request::command() +{ + return m_command; +} + + diff --git a/kopete/protocols/groupwise/libgroupwise/request.h b/kopete/protocols/groupwise/libgroupwise/request.h new file mode 100644 index 00000000..85a55e8a --- /dev/null +++ b/kopete/protocols/groupwise/libgroupwise/request.h @@ -0,0 +1,40 @@ +/* + request.h - Kopete Groupwise Protocol + + Copyright (c) 2004 SUSE Linux AG http://www.suse.com + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ +#ifndef LIBGW_REQUEST_H +#define LIBGW_REQUEST_H + +#include "usertransfer.h" + +/** + * Represents a client generated request to the server + * Create with @ref RequestFactory::request(). + * @author Kopete Developers +*/ +class Request : public UserTransfer +{ +friend class RequestFactory; + +public: + ~Request( ); + QString command(); + TransferType type() { return Transfer::RequestTransfer; } +private: + Request( const int transactionId, const QString &command ); + QString m_command; +}; + +#endif diff --git a/kopete/protocols/groupwise/libgroupwise/requestfactory.cpp b/kopete/protocols/groupwise/libgroupwise/requestfactory.cpp new file mode 100644 index 00000000..6387370f --- /dev/null +++ b/kopete/protocols/groupwise/libgroupwise/requestfactory.cpp @@ -0,0 +1,37 @@ +/* + requestfactory.cpp - Kopete Groupwise Protocol + + Copyright (c) 2004 SUSE Linux AG http://www.suse.com + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#include "request.h" + +#include "requestfactory.h" + +#define GW_REQUESTFACTORY_FIRST_TID 1 +RequestFactory::RequestFactory() +: m_nextTransaction( GW_REQUESTFACTORY_FIRST_TID ) +{ +} + +RequestFactory::~RequestFactory() +{ +} + +Request* RequestFactory::request( const QString &command ) +{ + return new Request( m_nextTransaction++, command ); +} + + diff --git a/kopete/protocols/groupwise/libgroupwise/requestfactory.h b/kopete/protocols/groupwise/libgroupwise/requestfactory.h new file mode 100644 index 00000000..e4ec073e --- /dev/null +++ b/kopete/protocols/groupwise/libgroupwise/requestfactory.h @@ -0,0 +1,44 @@ +/* + requestfactory.h - Kopete Groupwise Protocol + + Copyright (c) 2004 SUSE Linux AG http://www.suse.com + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#ifndef REQUESTFACTORY_H +#define REQUESTFACTORY_H + +#include <qstring.h> + +class Request; + +/** + * Factory for obtaining @ref Request instances. + * @author Kopete Developers + */ +class RequestFactory{ +public: + RequestFactory(); + ~RequestFactory(); + + /** + * Obtain a new @ref Request instance + * The consumer is responsible for deleting this + * TODO: Allow the user to provide the fields for the request in this call + */ + Request * request( const QString &request); +private: + int m_nextTransaction; +}; + +#endif diff --git a/kopete/protocols/groupwise/libgroupwise/response.cpp b/kopete/protocols/groupwise/libgroupwise/response.cpp new file mode 100644 index 00000000..837c7810 --- /dev/null +++ b/kopete/protocols/groupwise/libgroupwise/response.cpp @@ -0,0 +1,34 @@ +/* + response.cpp - Kopete Groupwise Protocol + + Copyright (c) 2004 SUSE Linux AG http://www.suse.com + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ +#include <qapplication.h> + +#include "response.h" + +Response::Response( int transactionId, int resultCode, Field::FieldList fields ) +: UserTransfer( transactionId ), m_resultCode( resultCode ) +{ + setFields( fields ); +} + +Response::~Response() +{ +} + +int Response::resultCode() const +{ + return m_resultCode; +} diff --git a/kopete/protocols/groupwise/libgroupwise/response.h b/kopete/protocols/groupwise/libgroupwise/response.h new file mode 100644 index 00000000..8f4fb970 --- /dev/null +++ b/kopete/protocols/groupwise/libgroupwise/response.h @@ -0,0 +1,39 @@ +/* + response.h - Kopete Groupwise Protocol + + Copyright (c) 2004 SUSE Linux AG http://www.suse.com + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#ifndef GW_RESPONSE_H +#define GW_RESPONSE_H + +#include "usertransfer.h" + +/** + * Represents the server's reply to a client generated request + * @author Kopete Developers +*/ +class Response: public UserTransfer +{ +public: + Response( int transactionId, int resultCode, Field::FieldList fields ); + virtual ~Response( ); + + TransferType type() { return Transfer::ResponseTransfer; } + int resultCode() const; +private: + int m_resultCode; +}; + +#endif diff --git a/kopete/protocols/groupwise/libgroupwise/responseprotocol.cpp b/kopete/protocols/groupwise/libgroupwise/responseprotocol.cpp new file mode 100644 index 00000000..6784fd15 --- /dev/null +++ b/kopete/protocols/groupwise/libgroupwise/responseprotocol.cpp @@ -0,0 +1,314 @@ +/* + Kopete Groupwise Protocol + responseprotocol.cpp - Protocol used for reading incoming GroupWise Responses + + Copyright (c) 2004 SUSE Linux AG http://www.suse.com + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#include <qbuffer.h> + +#include "response.h" + +#include "responseprotocol.h" + +ResponseProtocol::ResponseProtocol(QObject* parent, const char* name): InputProtocolBase(parent, name) +{ +} + + +ResponseProtocol::~ResponseProtocol() +{ +} + +Transfer * ResponseProtocol::parse( const QByteArray & wire, uint & bytes ) +{ + m_bytes = 0; + m_collatingFields.clear(); + //m_din = new QDataStream( wire, IO_ReadOnly ); + QBuffer inBuf( wire ); + inBuf.open( IO_ReadOnly); + m_din.setDevice( &inBuf ); + m_din.setByteOrder( QDataStream::LittleEndian ); + + // check that this begins with a HTTP (is a response) + Q_UINT32 val; + m_din >> val; + m_bytes += sizeof( Q_UINT32 ); + + Q_ASSERT( qstrncmp( (const char *)&val, "HTTP", strlen( "HTTP" ) ) == 0 ); + + // read rest of HTTP header and look for a 301 redirect. + QCString headerFirst; + if ( !readGroupWiseLine( headerFirst ) ) + return 0; + // pull out the HTTP return code + int firstSpace = headerFirst.find( ' ' ); + QString rtnField = headerFirst.mid( firstSpace, headerFirst.find( ' ', firstSpace + 1 ) ); + bool ok = true; + int rtnCode; + int packetState = -1; + rtnCode = rtnField.toInt( &ok ); + debug( "CoreProtocol::readResponse() got HTTP return code " ); + // read rest of header + QStringList headerRest; + QCString line; + while ( line != "\r\n" ) + { + if ( !readGroupWiseLine( line ) ) + { + m_din.unsetDevice(); + return 0; + } + headerRest.append( line ); + debug( QString( "- read header line - (%1) : %2" ).arg( line.length() ).arg( line.data() ) ); + } + debug( "ResponseProtocol::readResponse() header finished" ); + // if it's a redirect, set flag + if ( ok && rtnCode == 301 ) + { + debug( "- server redirect " ); + packetState = ServerRedirect; + m_din.unsetDevice(); + return 0; + } + // other header processing ( 500! ) + if ( ok && rtnCode == 500 ) + { + debug( QString( "- server error %1" ).arg( rtnCode ) ); + packetState = ServerError; + m_din.unsetDevice(); + return 0; + } + if ( ok && rtnCode == 404 ) + { + debug( QString( "- server error %1" ).arg( rtnCode ) ); + packetState = ServerError; + m_din.unsetDevice(); + return 0; + } + if ( m_din.atEnd() ) + { + debug( "- no fields" ); + packetState = ProtocolError; + m_din.unsetDevice(); + return 0; + } + + // read fields + if ( !readFields( -1 ) ) + { + m_din.unsetDevice(); + return 0; + } + // find transaction id field and create Response object if nonzero + int tId = 0; + int resultCode = 0; + Field::FieldListIterator it; + Field::FieldListIterator end = m_collatingFields.end(); + it = m_collatingFields.find( NM_A_SZ_TRANSACTION_ID ); + if ( it != end ) + { + Field::SingleField * sf = dynamic_cast<Field::SingleField*>( *it ); + if ( sf ) + { + tId = sf->value().toInt(); + debug( QString( "ResponseProtocol::readResponse() - transaction ID is %1" ).arg( tId ) ); + m_collatingFields.remove( it ); + delete sf; + } + } + it = m_collatingFields.find( NM_A_SZ_RESULT_CODE ); + if ( it != end ) + { + Field::SingleField * sf = dynamic_cast<Field::SingleField*>( *it ); + if ( sf ) + { + resultCode = sf->value().toInt(); + debug( QString( "ResponseProtocol::readResponse() - result code is %1" ).arg( resultCode ) ); + m_collatingFields.remove( it ); + delete sf; + } + } + // append to inQueue + if ( tId ) + { + debug( QString( "ResponseProtocol::readResponse() - setting state Available, got %1 fields in base array" ).arg(m_collatingFields.count() ) ); + packetState = Available; + bytes = m_bytes; + m_din.unsetDevice(); + return new Response( tId, resultCode, m_collatingFields ); + } + else + { + debug( "- WARNING - NO TRANSACTION ID FOUND!" ); + m_state = ProtocolError; + m_din.unsetDevice(); + m_collatingFields.purge(); + return 0; + } +} + +bool ResponseProtocol::readFields( int fieldCount, Field::FieldList * list ) +{ + // build a list of fields. + // If there is already a list of fields stored in m_collatingFields, + // the list we're reading on this iteration must be a nested list + // so when we're done reading it, add it to the MultiList element + // that is the last element in the top list in m_collatingFields. + // if we find the beginning of a new nested list, push the current list onto m_collatingFields + debug( "ResponseProtocol::readFields()" ); + if ( fieldCount > 0 ) + debug( QString( "reading %1 fields" ).arg( fieldCount ) ); + Field::FieldList currentList; + while ( fieldCount != 0 ) // prevents bad input data from ruining our day + { + // the field being read + // read field + Q_UINT8 type, method; + Q_UINT32 val; + QCString tag; + // read uint8 type + if ( !okToProceed() ) + { + currentList.purge(); + return false; + } + m_din >> type; + m_bytes += sizeof( Q_UINT8 ); + // if type is 0 SOMETHING_INVALID, we're at the end of the fields + if ( type == 0 ) /*&& m_din->atEnd() )*/ + { + debug( "- end of field list" ); + m_packetState = FieldsRead; + // do something to indicate we're done + break; + } + // read uint8 method + if ( !okToProceed() ) + { + currentList.purge(); + return false; + } + m_din >> method; + m_bytes += sizeof( Q_UINT8 ); + // read tag and length + if ( !safeReadBytes( tag, val ) ) + { + currentList.purge(); + return false; + } + + debug( QString( "- type: %1, method: %2, tag: %3," ).arg( type ).arg( method ).arg( tag.data() ) ); + // if multivalue or array + if ( type == NMFIELD_TYPE_MV || type == NMFIELD_TYPE_ARRAY ) + { + // read length uint32 + if ( !okToProceed() ) + { + currentList.purge(); + return false; + } + m_din >> val; + m_bytes += sizeof( Q_UINT32 ); + + // create multifield + debug( QString( " multi field containing: %1" ).arg( val ) ); + Field::MultiField* m = new Field::MultiField( tag, method, 0, type ); + currentList.append( m ); + if ( !readFields( val, ¤tList) ) + { + currentList.purge(); + return false; + } + } + else + { + + if ( type == NMFIELD_TYPE_UTF8 || type == NMFIELD_TYPE_DN ) + { + QCString rawData; + if( !safeReadBytes( rawData, val ) ) + { + currentList.purge(); + return false; + } + if ( val > NMFIELD_MAX_STR_LENGTH ) + { + m_packetState = ProtocolError; + break; + } + // convert to unicode - ignore the terminating NUL, because Qt<3.3.2 doesn't sanity check val. + QString fieldValue = QString::fromUtf8( rawData.data(), val - 1 ); + debug( QString( "- utf/dn single field: %1" ).arg( fieldValue ) ); + // create singlefield + Field::SingleField* s = new Field::SingleField( tag, method, 0, type, fieldValue ); + currentList.append( s ); + } + else + { + // otherwise ( numeric ) + // read value uint32 + if ( !okToProceed() ) + { + currentList.purge(); + return false; + } + m_din >> val; + m_bytes += sizeof( Q_UINT32 ); + debug( QString( "- numeric field: %1" ).arg( val ) ); + Field::SingleField* s = new Field::SingleField( tag, method, 0, type, val ); + currentList.append( s ); + } + } + // decrease the fieldCount if we're using it + if ( fieldCount > 0 ) + fieldCount--; + } + // got a whole list! + // if fieldCount == 0, we've just read a whole nested list, so add this list to the last element in 'list' + if ( fieldCount == 0 && list ) + { + debug( "- finished reading nested list" ); + Field::MultiField * m = dynamic_cast<Field::MultiField*>( list->last() ); + m->setFields( currentList ); + } + + // if fieldCount == -1; we're done reading the top level fieldlist, so store it. + if ( fieldCount == -1 ) + { + debug( "- finished reading ALL FIELDS!" ); + m_collatingFields = currentList; + } + return true; +} + +bool ResponseProtocol::readGroupWiseLine( QCString & line ) +{ + line = QCString(); + while ( true ) + { + Q_UINT8 c; + + if (! okToProceed() ) + return false; + m_din >> c; + m_bytes++; + line += QChar(c); + if ( c == '\n' ) + break; + } + return true; +} + +#include "responseprotocol.moc" diff --git a/kopete/protocols/groupwise/libgroupwise/responseprotocol.h b/kopete/protocols/groupwise/libgroupwise/responseprotocol.h new file mode 100644 index 00000000..5957ad19 --- /dev/null +++ b/kopete/protocols/groupwise/libgroupwise/responseprotocol.h @@ -0,0 +1,76 @@ +/* + Kopete Groupwise Protocol + responseprotocol.h - Protocol used for reading incoming GroupWise Responses + + Copyright (c) 2004 SUSE Linux AG http://www.suse.com + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#ifndef RESPONSEPROTOCOL_H +#define RESPONSEPROTOCOL_H + +#include "gwerror.h" +#include "gwfield.h" + +#include "inputprotocolbase.h" + +/** +Handles the parsing of incoming Response messages + +@author Kopete Developers +*/ +class ResponseProtocol : public InputProtocolBase +{ +Q_OBJECT +public: + /** + * Describes the current state of the protocol + */ + enum State { NeedMore, Available, ServerError, ServerRedirect, ReadingEvent, NoData }; + + /** + * Describes the parsing of the last received packet + */ + enum PacketState { FieldsRead, ProtocolError }; + + ResponseProtocol(QObject* parent, const char* name); + ~ResponseProtocol(); + /** + * Attempt to parse the supplied data into an @ref Response object. + * The exact state of the parse attempt can be read using @ref state. + * @param rawData The unparsed data. + * @param bytes An integer used to return the number of bytes read. + * @return A pointer to an Response object if successfull, otherwise 0. The caller is responsible for deleting this object. + */ + Transfer * parse( const QByteArray &, uint & bytes ); +protected: + /** + * read a line ending in \r\n, including the \r\n + */ + bool readGroupWiseLine( QCString & ); + /** + * Read in a response + */ + bool readResponse(); + /** + * Parse received fields and store in m_collatingFields + */ + bool readFields( int fieldCount, Field::FieldList * list = 0 ); +private: + // fields from a packet being parsed, before it has been completely received + //QValueStack<Field::FieldList> m_collatingFields; + Field::FieldList m_collatingFields; + int m_packetState; // represents the state of the parsing of the last incoming data received +}; + +#endif diff --git a/kopete/protocols/groupwise/libgroupwise/rtf.cc b/kopete/protocols/groupwise/libgroupwise/rtf.cc new file mode 100644 index 00000000..eb5da80e --- /dev/null +++ b/kopete/protocols/groupwise/libgroupwise/rtf.cc @@ -0,0 +1,2532 @@ +#line 2 "rtf.cc" + +#line 4 "rtf.cc" + +#define YY_INT_ALIGNED short int + +/* A lexical scanner generated by flex */ + +#define FLEX_SCANNER +#define YY_FLEX_MAJOR_VERSION 2 +#define YY_FLEX_MINOR_VERSION 5 +#define YY_FLEX_SUBMINOR_VERSION 31 +#if YY_FLEX_SUBMINOR_VERSION > 0 +#define FLEX_BETA +#endif + +/* First, we deal with platform-specific or compiler-specific issues. */ + +/* begin standard C headers. */ +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <stdlib.h> + +/* end standard C headers. */ + +/* flex integer type definitions */ + +#ifndef FLEXINT_H +#define FLEXINT_H + +/* C99 systems have <inttypes.h>. Non-C99 systems may or may not. */ + +#if defined __STDC_VERSION__ && __STDC_VERSION__ >= 199901L +#include <inttypes.h> +typedef int8_t flex_int8_t; +typedef uint8_t flex_uint8_t; +typedef int16_t flex_int16_t; +typedef uint16_t flex_uint16_t; +typedef int32_t flex_int32_t; +typedef uint32_t flex_uint32_t; +#else +typedef signed char flex_int8_t; +typedef short int flex_int16_t; +typedef int flex_int32_t; +typedef unsigned char flex_uint8_t; +typedef unsigned short int flex_uint16_t; +typedef unsigned int flex_uint32_t; +#endif /* ! C99 */ + +/* Limits of integral types. */ +#ifndef INT8_MIN +#define INT8_MIN (-128) +#endif +#ifndef INT16_MIN +#define INT16_MIN (-32767-1) +#endif +#ifndef INT32_MIN +#define INT32_MIN (-2147483647-1) +#endif +#ifndef INT8_MAX +#define INT8_MAX (127) +#endif +#ifndef INT16_MAX +#define INT16_MAX (32767) +#endif +#ifndef INT32_MAX +#define INT32_MAX (2147483647) +#endif +#ifndef UINT8_MAX +#define UINT8_MAX (255U) +#endif +#ifndef UINT16_MAX +#define UINT16_MAX (65535U) +#endif +#ifndef UINT32_MAX +#define UINT32_MAX (4294967295U) +#endif + +#endif /* ! FLEXINT_H */ + +#ifdef __cplusplus + +/* The "const" storage-class-modifier is valid. */ +#define YY_USE_CONST + +#else /* ! __cplusplus */ + +#if __STDC__ + +#define YY_USE_CONST + +#endif /* __STDC__ */ +#endif /* ! __cplusplus */ + +#ifdef YY_USE_CONST +#define yyconst const +#else +#define yyconst +#endif + +/* Returned upon end-of-file. */ +#define YY_NULL 0 + +/* Promotes a possibly negative, possibly signed char to an unsigned + * integer for use as an array index. If the signed char is negative, + * we want to instead treat it as an 8-bit unsigned char, hence the + * double cast. + */ +#define YY_SC_TO_UI(c) ((unsigned int) (unsigned char) c) + +/* Enter a start condition. This macro really ought to take a parameter, + * but we do it the disgusting crufty way forced on us by the ()-less + * definition of BEGIN. + */ +#define BEGIN (yy_start) = 1 + 2 * + +/* Translate the current start state into a value that can be later handed + * to BEGIN to return to the state. The YYSTATE alias is for lex + * compatibility. + */ +#define YY_START (((yy_start) - 1) / 2) +#define YYSTATE YY_START + +/* Action number for EOF rule of a given start state. */ +#define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1) + +/* Special action meaning "start processing a new file". */ +#define YY_NEW_FILE rtfrestart(rtfin ) + +#define YY_END_OF_BUFFER_CHAR 0 + +/* Size of default input buffer. */ +#ifndef YY_BUF_SIZE +#define YY_BUF_SIZE 16384 +#endif + +#ifndef YY_TYPEDEF_YY_BUFFER_STATE +#define YY_TYPEDEF_YY_BUFFER_STATE +typedef struct yy_buffer_state *YY_BUFFER_STATE; +#endif + +extern int rtfleng; + +extern FILE *rtfin, *rtfout; + +#define EOB_ACT_CONTINUE_SCAN 0 +#define EOB_ACT_END_OF_FILE 1 +#define EOB_ACT_LAST_MATCH 2 + + #define YY_LESS_LINENO(n) + +/* Return all but the first "n" matched characters back to the input stream. */ +#define yyless(n) \ + do \ + { \ + /* Undo effects of setting up rtftext. */ \ + int yyless_macro_arg = (n); \ + YY_LESS_LINENO(yyless_macro_arg);\ + *yy_cp = (yy_hold_char); \ + YY_RESTORE_YY_MORE_OFFSET \ + (yy_c_buf_p) = yy_cp = yy_bp + yyless_macro_arg - YY_MORE_ADJ; \ + YY_DO_BEFORE_ACTION; /* set up rtftext again */ \ + } \ + while ( 0 ) + +#define unput(c) yyunput( c, (yytext_ptr) ) + +/* The following is because we cannot portably get our hands on size_t + * (without autoconf's help, which isn't available because we want + * flex-generated scanners to compile on their own). + */ + +#ifndef YY_TYPEDEF_YY_SIZE_T +#define YY_TYPEDEF_YY_SIZE_T +typedef unsigned int yy_size_t; +#endif + +#ifndef YY_STRUCT_YY_BUFFER_STATE +#define YY_STRUCT_YY_BUFFER_STATE +struct yy_buffer_state + { + FILE *yy_input_file; + + char *yy_ch_buf; /* input buffer */ + char *yy_buf_pos; /* current position in input buffer */ + + /* Size of input buffer in bytes, not including room for EOB + * characters. + */ + yy_size_t yy_buf_size; + + /* Number of characters read into yy_ch_buf, not including EOB + * characters. + */ + int yy_n_chars; + + /* Whether we "own" the buffer - i.e., we know we created it, + * and can realloc() it to grow it, and should free() it to + * delete it. + */ + int yy_is_our_buffer; + + /* Whether this is an "interactive" input source; if so, and + * if we're using stdio for input, then we want to use getc() + * instead of fread(), to make sure we stop fetching input after + * each newline. + */ + int yy_is_interactive; + + /* Whether we're considered to be at the beginning of a line. + * If so, '^' rules will be active on the next match, otherwise + * not. + */ + int yy_at_bol; + + int yy_bs_lineno; /**< The line count. */ + int yy_bs_column; /**< The column count. */ + + /* Whether to try to fill the input buffer when we reach the + * end of it. + */ + int yy_fill_buffer; + + int yy_buffer_status; + +#define YY_BUFFER_NEW 0 +#define YY_BUFFER_NORMAL 1 + /* When an EOF's been seen but there's still some text to process + * then we mark the buffer as YY_EOF_PENDING, to indicate that we + * shouldn't try reading from the input source any more. We might + * still have a bunch of tokens to match, though, because of + * possible backing-up. + * + * When we actually see the EOF, we change the status to "new" + * (via rtfrestart()), so that the user can continue scanning by + * just pointing rtfin at a new input file. + */ +#define YY_BUFFER_EOF_PENDING 2 + + }; +#endif /* !YY_STRUCT_YY_BUFFER_STATE */ + +/* Stack of input buffers. */ +static size_t yy_buffer_stack_top = 0; /**< index of top of stack. */ +static size_t yy_buffer_stack_max = 0; /**< capacity of stack. */ +static YY_BUFFER_STATE * yy_buffer_stack = 0; /**< Stack as an array. */ + +/* We provide macros for accessing buffer states in case in the + * future we want to put the buffer states in a more general + * "scanner state". + * + * Returns the top of the stack, or NULL. + */ +#define YY_CURRENT_BUFFER ( (yy_buffer_stack) \ + ? (yy_buffer_stack)[(yy_buffer_stack_top)] \ + : NULL) + +/* Same as previous macro, but useful when we know that the buffer stack is not + * NULL or when we need an lvalue. For internal use only. + */ +#define YY_CURRENT_BUFFER_LVALUE (yy_buffer_stack)[(yy_buffer_stack_top)] + +/* yy_hold_char holds the character lost when rtftext is formed. */ +static char yy_hold_char; +static int yy_n_chars; /* number of characters read into yy_ch_buf */ +int rtfleng; + +/* Points to current character in buffer. */ +static char *yy_c_buf_p = (char *) 0; +static int yy_init = 1; /* whether we need to initialize */ +static int yy_start = 0; /* start state number */ + +/* Flag which is used to allow rtfwrap()'s to do buffer switches + * instead of setting up a fresh rtfin. A bit of a hack ... + */ +static int yy_did_buffer_switch_on_eof; + +void rtfrestart (FILE *input_file ); +void rtf_switch_to_buffer (YY_BUFFER_STATE new_buffer ); +YY_BUFFER_STATE rtf_create_buffer (FILE *file,int size ); +void rtf_delete_buffer (YY_BUFFER_STATE b ); +void rtf_flush_buffer (YY_BUFFER_STATE b ); +void rtfpush_buffer_state (YY_BUFFER_STATE new_buffer ); +void rtfpop_buffer_state (void ); + +static void rtfensure_buffer_stack (void ); +static void rtf_load_buffer_state (void ); +static void rtf_init_buffer (YY_BUFFER_STATE b,FILE *file ); + +#define YY_FLUSH_BUFFER rtf_flush_buffer(YY_CURRENT_BUFFER ) + +YY_BUFFER_STATE rtf_scan_buffer (char *base,yy_size_t size ); +YY_BUFFER_STATE rtf_scan_string (yyconst char *yy_str ); +YY_BUFFER_STATE rtf_scan_bytes (yyconst char *bytes,int len ); + +void *rtfalloc (yy_size_t ); +void *rtfrealloc (void *,yy_size_t ); +void rtffree (void * ); + +#define yy_new_buffer rtf_create_buffer + +#define yy_set_interactive(is_interactive) \ + { \ + if ( ! YY_CURRENT_BUFFER ){ \ + rtfensure_buffer_stack (); \ + YY_CURRENT_BUFFER_LVALUE = \ + rtf_create_buffer(rtfin,YY_BUF_SIZE ); \ + } \ + YY_CURRENT_BUFFER_LVALUE->yy_is_interactive = is_interactive; \ + } + +#define yy_set_bol(at_bol) \ + { \ + if ( ! YY_CURRENT_BUFFER ){\ + rtfensure_buffer_stack (); \ + YY_CURRENT_BUFFER_LVALUE = \ + rtf_create_buffer(rtfin,YY_BUF_SIZE ); \ + } \ + YY_CURRENT_BUFFER_LVALUE->yy_at_bol = at_bol; \ + } + +#define YY_AT_BOL() (YY_CURRENT_BUFFER_LVALUE->yy_at_bol) + +/* Begin user sect3 */ + +typedef unsigned char YY_CHAR; + +FILE *rtfin = (FILE *) 0, *rtfout = (FILE *) 0; + +typedef int yy_state_type; + +extern int rtflineno; + +int rtflineno = 1; + +extern char *rtftext; +#define yytext_ptr rtftext + +static yy_state_type yy_get_previous_state (void ); +static yy_state_type yy_try_NUL_trans (yy_state_type current_state ); +static int yy_get_next_buffer (void ); +static void yy_fatal_error (yyconst char msg[] ); + +/* Done after the current pattern has been matched and before the + * corresponding action - sets up rtftext. + */ +#define YY_DO_BEFORE_ACTION \ + (yytext_ptr) = yy_bp; \ + rtfleng = (size_t) (yy_cp - yy_bp); \ + (yy_hold_char) = *yy_cp; \ + *yy_cp = '\0'; \ + (yy_c_buf_p) = yy_cp; + +#define YY_NUM_RULES 10 +#define YY_END_OF_BUFFER 11 +/* This struct is not used in this scanner, + but its presence is necessary. */ +struct yy_trans_info + { + flex_int32_t yy_verify; + flex_int32_t yy_nxt; + }; +static yyconst flex_int16_t yy_accept[33] = + { 0, + 0, 0, 11, 8, 8, 9, 9, 1, 2, 8, + 0, 0, 5, 3, 5, 0, 0, 5, 5, 5, + 0, 6, 5, 7, 5, 5, 5, 4, 5, 5, + 5, 0 + } ; + +static yyconst flex_int32_t yy_ec[256] = + { 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 3, 1, 1, 4, 1, 1, 1, 5, 1, + 1, 1, 1, 1, 1, 1, 1, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 1, 1, 7, + 1, 8, 9, 1, 10, 10, 10, 10, 10, 10, + 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, + 1, 12, 1, 1, 1, 1, 10, 10, 10, 10, + + 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 13, 11, 11, 11, + 11, 11, 14, 1, 15, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1 + } ; + +static yyconst flex_int32_t yy_meta[16] = + { 0, + 1, 1, 2, 1, 1, 2, 3, 4, 1, 2, + 2, 3, 2, 3, 3 + } ; + +static yyconst flex_int16_t yy_base[37] = + { 0, + 0, 14, 45, 0, 0, 39, 25, 59, 59, 0, + 38, 0, 2, 59, 14, 0, 3, 59, 16, 21, + 25, 59, 28, 59, 38, 23, 19, 59, 17, 12, + 5, 59, 47, 51, 1, 55 + } ; + +static yyconst flex_int16_t yy_def[37] = + { 0, + 33, 33, 32, 34, 34, 32, 32, 32, 32, 34, + 32, 32, 35, 32, 35, 36, 32, 32, 32, 32, + 36, 32, 32, 32, 32, 32, 25, 32, 25, 25, + 25, 0, 32, 32, 32, 32 + } ; + +static yyconst flex_int16_t yy_nxt[75] = + { 0, + 32, 5, 13, 32, 18, 17, 6, 19, 22, 17, + 19, 7, 22, 8, 9, 5, 18, 31, 18, 20, + 6, 19, 30, 18, 29, 7, 23, 8, 9, 12, + 18, 28, 24, 25, 13, 13, 14, 15, 14, 14, + 26, 16, 11, 27, 32, 32, 28, 4, 4, 4, + 4, 10, 10, 32, 10, 21, 21, 21, 3, 32, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32 + } ; + +static yyconst flex_int16_t yy_chk[75] = + { 0, + 0, 1, 35, 0, 13, 12, 1, 13, 17, 12, + 31, 1, 17, 1, 1, 2, 15, 30, 19, 15, + 2, 19, 29, 20, 27, 2, 20, 2, 2, 7, + 23, 26, 21, 23, 7, 7, 7, 7, 7, 7, + 25, 11, 6, 25, 3, 0, 25, 33, 33, 33, + 33, 34, 34, 0, 34, 36, 36, 36, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32 + } ; + +static yy_state_type yy_last_accepting_state; +static char *yy_last_accepting_cpos; + +extern int rtf_flex_debug; +int rtf_flex_debug = 0; + +/* The intent behind this definition is that it'll catch + * any uses of REJECT which flex missed. + */ +#define REJECT reject_used_but_not_detected +#define yymore() yymore_used_but_not_detected +#define YY_MORE_ADJ 0 +#define YY_RESTORE_YY_MORE_OFFSET +char *rtftext; +#line 1 "rtf.ll" +#line 2 "rtf.ll" +/* + rtf.ll - A simple RTF Parser (Flex code) + + Copyright (c) 2002 by Vladimir Shutoff <vovan@shutoff.ru> (original code) + Copyright (c) 2004 by Thiago S. Barcelos <barcelos@ime.usp.br> (Kopete port) + Kopete (c) 2002-2003 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ************************************************************************* + +update rtf.cc: +flex -olex.yy.c `test -f rtf.ll || echo './'`rtf.ll +sed '/^#/ s|lex.yy\.c|rtf.cc|' lex.yy.c >rtf.cc +rm -f lex.yy.c + +*/ + +#define UP 1 +#define DOWN 2 +#define CMD 3 +#define TXT 4 +#define HEX 5 +#define IMG 6 +#define UNICODE_CHAR 7 +#define SKIP 8 +#define SLASH 9 +#define S_TXT 10 + +#define YY_NEVER_INTERACTIVE 1 +#define YY_ALWAYS_INTERACTIVE 0 +#define YY_MAIN 0 + +#line 505 "rtf.cc" + +#define INITIAL 0 + +#ifndef YY_NO_UNISTD_H +/* Special case for "unistd.h", since it is non-ANSI. We include it way + * down here because we want the user's section 1 to have been scanned first. + * The user has a chance to override it with an option. + */ +#include <unistd.h> +#endif + +#ifndef YY_EXTRA_TYPE +#define YY_EXTRA_TYPE void * +#endif + +/* Macros after this point can all be overridden by user definitions in + * section 1. + */ + +#ifndef YY_SKIP_YYWRAP +#ifdef __cplusplus +extern "C" int rtfwrap (void ); +#else +extern int rtfwrap (void ); +#endif +#endif + +#ifndef yytext_ptr +static void yy_flex_strncpy (char *,yyconst char *,int ); +#endif + +#ifdef YY_NEED_STRLEN +static int yy_flex_strlen (yyconst char * ); +#endif + +#ifndef YY_NO_INPUT + +#ifdef __cplusplus +static int yyinput (void ); +#else +static int input (void ); +#endif + +#endif + +/* Amount of stuff to slurp up with each read. */ +#ifndef YY_READ_BUF_SIZE +#define YY_READ_BUF_SIZE 8192 +#endif + +/* Copy whatever the last rule matched to the standard output. */ +#ifndef ECHO +/* This used to be an fputs(), but since the string might contain NUL's, + * we now use fwrite(). + */ +#define ECHO (void) fwrite( rtftext, rtfleng, 1, rtfout ) +#endif + +/* Gets input and stuffs it into "buf". number of characters read, or YY_NULL, + * is returned in "result". + */ +#ifndef YY_INPUT +#define YY_INPUT(buf,result,max_size) \ + if ( YY_CURRENT_BUFFER_LVALUE->yy_is_interactive ) \ + { \ + int c = '*'; \ + size_t n; \ + for ( n = 0; n < max_size && \ + (c = getc( rtfin )) != EOF && c != '\n'; ++n ) \ + buf[n] = (char) c; \ + if ( c == '\n' ) \ + buf[n++] = (char) c; \ + if ( c == EOF && ferror( rtfin ) ) \ + YY_FATAL_ERROR( "input in flex scanner failed" ); \ + result = n; \ + } \ + else \ + { \ + errno=0; \ + while ( (result = fread(buf, 1, max_size, rtfin))==0 && ferror(rtfin)) \ + { \ + if( errno != EINTR) \ + { \ + YY_FATAL_ERROR( "input in flex scanner failed" ); \ + break; \ + } \ + errno=0; \ + clearerr(rtfin); \ + } \ + }\ +\ + +#endif + +/* No semi-colon after return; correct usage is to write "yyterminate();" - + * we don't want an extra ';' after the "return" because that will cause + * some compilers to complain about unreachable statements. + */ +#ifndef yyterminate +#define yyterminate() return YY_NULL +#endif + +/* Number of entries by which start-condition stack grows. */ +#ifndef YY_START_STACK_INCR +#define YY_START_STACK_INCR 25 +#endif + +/* Report a fatal error. */ +#ifndef YY_FATAL_ERROR +#define YY_FATAL_ERROR(msg) yy_fatal_error( msg ) +#endif + +/* end tables serialization structures and prototypes */ + +/* Default declaration of generated scanner - a define so the user can + * easily add parameters. + */ +#ifndef YY_DECL +#define YY_DECL_IS_OURS 1 + +extern int rtflex (void); + +#define YY_DECL int rtflex (void) +#endif /* !YY_DECL */ + +/* Code executed at the beginning of each rule, after rtftext and rtfleng + * have been set up. + */ +#ifndef YY_USER_ACTION +#define YY_USER_ACTION +#endif + +/* Code executed at the end of each rule. */ +#ifndef YY_BREAK +#define YY_BREAK break; +#endif + +#define YY_RULE_SETUP \ + YY_USER_ACTION + +/** The main scanner function which does all the work. + */ +YY_DECL +{ + register yy_state_type yy_current_state; + register char *yy_cp, *yy_bp; + register int yy_act; + +#line 46 "rtf.ll" + + +#line 657 "rtf.cc" + + if ( (yy_init) ) + { + (yy_init) = 0; + +#ifdef YY_USER_INIT + YY_USER_INIT; +#endif + + if ( ! (yy_start) ) + (yy_start) = 1; /* first start state */ + + if ( ! rtfin ) + rtfin = stdin; + + if ( ! rtfout ) + rtfout = stdout; + + if ( ! YY_CURRENT_BUFFER ) { + rtfensure_buffer_stack (); + YY_CURRENT_BUFFER_LVALUE = + rtf_create_buffer(rtfin,YY_BUF_SIZE ); + } + + rtf_load_buffer_state( ); + } + + while ( 1 ) /* loops until end-of-file is reached */ + { + yy_cp = (yy_c_buf_p); + + /* Support of rtftext. */ + *yy_cp = (yy_hold_char); + + /* yy_bp points to the position in yy_ch_buf of the start of + * the current run. + */ + yy_bp = yy_cp; + + yy_current_state = (yy_start); +yy_match: + do + { + register YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)]; + if ( yy_accept[yy_current_state] ) + { + (yy_last_accepting_state) = yy_current_state; + (yy_last_accepting_cpos) = yy_cp; + } + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = (int) yy_def[yy_current_state]; + if ( yy_current_state >= 33 ) + yy_c = yy_meta[(unsigned int) yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; + ++yy_cp; + } + while ( yy_base[yy_current_state] != 59 ); + +yy_find_action: + yy_act = yy_accept[yy_current_state]; + if ( yy_act == 0 ) + { /* have to back up */ + yy_cp = (yy_last_accepting_cpos); + yy_current_state = (yy_last_accepting_state); + yy_act = yy_accept[yy_current_state]; + } + + YY_DO_BEFORE_ACTION; + +do_action: /* This label is used only to access EOF actions. */ + + switch ( yy_act ) + { /* beginning of action switch */ + case 0: /* must back up */ + /* undo the effects of YY_DO_BEFORE_ACTION */ + *yy_cp = (yy_hold_char); + yy_cp = (yy_last_accepting_cpos); + yy_current_state = (yy_last_accepting_state); + goto yy_find_action; + +case 1: +YY_RULE_SETUP +#line 48 "rtf.ll" +{ return UP; } + YY_BREAK +case 2: +YY_RULE_SETUP +#line 49 "rtf.ll" +{ return DOWN; } + YY_BREAK +case 3: +YY_RULE_SETUP +#line 50 "rtf.ll" +{ return SLASH; } + YY_BREAK +case 4: +YY_RULE_SETUP +#line 51 "rtf.ll" +{ return UNICODE_CHAR; } + YY_BREAK +case 5: +YY_RULE_SETUP +#line 52 "rtf.ll" +{ return CMD; } + YY_BREAK +case 6: +YY_RULE_SETUP +#line 53 "rtf.ll" +{ return HEX; } + YY_BREAK +case 7: +/* rule 7 can match eol */ +YY_RULE_SETUP +#line 54 "rtf.ll" +{ return IMG; } + YY_BREAK +case 8: +/* rule 8 can match eol */ +YY_RULE_SETUP +#line 55 "rtf.ll" +{ return TXT; } + YY_BREAK +case 9: +YY_RULE_SETUP +#line 56 "rtf.ll" +{ return TXT; } + YY_BREAK +case 10: +YY_RULE_SETUP +#line 57 "rtf.ll" +ECHO; + YY_BREAK +#line 792 "rtf.cc" +case YY_STATE_EOF(INITIAL): + yyterminate(); + + case YY_END_OF_BUFFER: + { + /* Amount of text matched not including the EOB char. */ + int yy_amount_of_matched_text = (int) (yy_cp - (yytext_ptr)) - 1; + + /* Undo the effects of YY_DO_BEFORE_ACTION. */ + *yy_cp = (yy_hold_char); + YY_RESTORE_YY_MORE_OFFSET + + if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_NEW ) + { + /* We're scanning a new file or input source. It's + * possible that this happened because the user + * just pointed rtfin at a new source and called + * rtflex(). If so, then we have to assure + * consistency between YY_CURRENT_BUFFER and our + * globals. Here is the right place to do so, because + * this is the first action (other than possibly a + * back-up) that will match for the new input source. + */ + (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; + YY_CURRENT_BUFFER_LVALUE->yy_input_file = rtfin; + YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_NORMAL; + } + + /* Note that here we test for yy_c_buf_p "<=" to the position + * of the first EOB in the buffer, since yy_c_buf_p will + * already have been incremented past the NUL character + * (since all states make transitions on EOB to the + * end-of-buffer state). Contrast this with the test + * in input(). + */ + if ( (yy_c_buf_p) <= &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] ) + { /* This was really a NUL. */ + yy_state_type yy_next_state; + + (yy_c_buf_p) = (yytext_ptr) + yy_amount_of_matched_text; + + yy_current_state = yy_get_previous_state( ); + + /* Okay, we're now positioned to make the NUL + * transition. We couldn't have + * yy_get_previous_state() go ahead and do it + * for us because it doesn't know how to deal + * with the possibility of jamming (and we don't + * want to build jamming into it because then it + * will run more slowly). + */ + + yy_next_state = yy_try_NUL_trans( yy_current_state ); + + yy_bp = (yytext_ptr) + YY_MORE_ADJ; + + if ( yy_next_state ) + { + /* Consume the NUL. */ + yy_cp = ++(yy_c_buf_p); + yy_current_state = yy_next_state; + goto yy_match; + } + + else + { + yy_cp = (yy_c_buf_p); + goto yy_find_action; + } + } + + else switch ( yy_get_next_buffer( ) ) + { + case EOB_ACT_END_OF_FILE: + { + (yy_did_buffer_switch_on_eof) = 0; + + if ( rtfwrap( ) ) + { + /* Note: because we've taken care in + * yy_get_next_buffer() to have set up + * rtftext, we can now set up + * yy_c_buf_p so that if some total + * hoser (like flex itself) wants to + * call the scanner after we return the + * YY_NULL, it'll still work - another + * YY_NULL will get returned. + */ + (yy_c_buf_p) = (yytext_ptr) + YY_MORE_ADJ; + + yy_act = YY_STATE_EOF(YY_START); + goto do_action; + } + + else + { + if ( ! (yy_did_buffer_switch_on_eof) ) + YY_NEW_FILE; + } + break; + } + + case EOB_ACT_CONTINUE_SCAN: + (yy_c_buf_p) = + (yytext_ptr) + yy_amount_of_matched_text; + + yy_current_state = yy_get_previous_state( ); + + yy_cp = (yy_c_buf_p); + yy_bp = (yytext_ptr) + YY_MORE_ADJ; + goto yy_match; + + case EOB_ACT_LAST_MATCH: + (yy_c_buf_p) = + &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)]; + + yy_current_state = yy_get_previous_state( ); + + yy_cp = (yy_c_buf_p); + yy_bp = (yytext_ptr) + YY_MORE_ADJ; + goto yy_find_action; + } + break; + } + + default: + YY_FATAL_ERROR( + "fatal flex scanner internal error--no action found" ); + } /* end of action switch */ + } /* end of scanning one token */ +} /* end of rtflex */ + +/* yy_get_next_buffer - try to read in a new buffer + * + * Returns a code representing an action: + * EOB_ACT_LAST_MATCH - + * EOB_ACT_CONTINUE_SCAN - continue scanning from current position + * EOB_ACT_END_OF_FILE - end of file + */ +static int yy_get_next_buffer (void) +{ + register char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf; + register char *source = (yytext_ptr); + register int number_to_move, i; + int ret_val; + + if ( (yy_c_buf_p) > &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] ) + YY_FATAL_ERROR( + "fatal flex scanner internal error--end of buffer missed" ); + + if ( YY_CURRENT_BUFFER_LVALUE->yy_fill_buffer == 0 ) + { /* Don't try to fill the buffer, so this is an EOF. */ + if ( (yy_c_buf_p) - (yytext_ptr) - YY_MORE_ADJ == 1 ) + { + /* We matched a single character, the EOB, so + * treat this as a final EOF. + */ + return EOB_ACT_END_OF_FILE; + } + + else + { + /* We matched some text prior to the EOB, first + * process it. + */ + return EOB_ACT_LAST_MATCH; + } + } + + /* Try to read more data. */ + + /* First move last chars to start of buffer. */ + number_to_move = (int) ((yy_c_buf_p) - (yytext_ptr)) - 1; + + for ( i = 0; i < number_to_move; ++i ) + *(dest++) = *(source++); + + if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_EOF_PENDING ) + /* don't do the read, it's not guaranteed to return an EOF, + * just force an EOF + */ + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars) = 0; + + else + { + size_t num_to_read = + YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1; + + while ( num_to_read <= 0 ) + { /* Not enough room in the buffer - grow it. */ + + /* just a shorter name for the current buffer */ + YY_BUFFER_STATE b = YY_CURRENT_BUFFER; + + int yy_c_buf_p_offset = + (int) ((yy_c_buf_p) - b->yy_ch_buf); + + if ( b->yy_is_our_buffer ) + { + int new_size = b->yy_buf_size * 2; + + if ( new_size <= 0 ) + b->yy_buf_size += b->yy_buf_size / 8; + else + b->yy_buf_size *= 2; + + b->yy_ch_buf = (char *) + /* Include room in for 2 EOB chars. */ + rtfrealloc((void *) b->yy_ch_buf,b->yy_buf_size + 2 ); + } + else + /* Can't grow it, we don't own it. */ + b->yy_ch_buf = 0; + + if ( ! b->yy_ch_buf ) + YY_FATAL_ERROR( + "fatal error - scanner input buffer overflow" ); + + (yy_c_buf_p) = &b->yy_ch_buf[yy_c_buf_p_offset]; + + num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size - + number_to_move - 1; + + } + + if ( num_to_read > YY_READ_BUF_SIZE ) + num_to_read = YY_READ_BUF_SIZE; + + /* Read in more data. */ + YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]), + (yy_n_chars), num_to_read ); + + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars); + } + + if ( (yy_n_chars) == 0 ) + { + if ( number_to_move == YY_MORE_ADJ ) + { + ret_val = EOB_ACT_END_OF_FILE; + rtfrestart(rtfin ); + } + + else + { + ret_val = EOB_ACT_LAST_MATCH; + YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = + YY_BUFFER_EOF_PENDING; + } + } + + else + ret_val = EOB_ACT_CONTINUE_SCAN; + + (yy_n_chars) += number_to_move; + YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] = YY_END_OF_BUFFER_CHAR; + YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] = YY_END_OF_BUFFER_CHAR; + + (yytext_ptr) = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[0]; + + return ret_val; +} + +/* yy_get_previous_state - get the state just before the EOB char was reached */ + + static yy_state_type yy_get_previous_state (void) +{ + register yy_state_type yy_current_state; + register char *yy_cp; + + yy_current_state = (yy_start); + + for ( yy_cp = (yytext_ptr) + YY_MORE_ADJ; yy_cp < (yy_c_buf_p); ++yy_cp ) + { + register YY_CHAR yy_c = (*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 1); + if ( yy_accept[yy_current_state] ) + { + (yy_last_accepting_state) = yy_current_state; + (yy_last_accepting_cpos) = yy_cp; + } + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = (int) yy_def[yy_current_state]; + if ( yy_current_state >= 33 ) + yy_c = yy_meta[(unsigned int) yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; + } + + return yy_current_state; +} + +/* yy_try_NUL_trans - try to make a transition on the NUL character + * + * synopsis + * next_state = yy_try_NUL_trans( current_state ); + */ + static yy_state_type yy_try_NUL_trans (yy_state_type yy_current_state ) +{ + register int yy_is_jam; + register char *yy_cp = (yy_c_buf_p); + + register YY_CHAR yy_c = 1; + if ( yy_accept[yy_current_state] ) + { + (yy_last_accepting_state) = yy_current_state; + (yy_last_accepting_cpos) = yy_cp; + } + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = (int) yy_def[yy_current_state]; + if ( yy_current_state >= 33 ) + yy_c = yy_meta[(unsigned int) yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; + yy_is_jam = (yy_current_state == 32); + + return yy_is_jam ? 0 : yy_current_state; +} + +#ifndef YY_NO_INPUT +#ifdef __cplusplus + static int yyinput (void) +#else + static int input (void) +#endif + +{ + int c; + + *(yy_c_buf_p) = (yy_hold_char); + + if ( *(yy_c_buf_p) == YY_END_OF_BUFFER_CHAR ) + { + /* yy_c_buf_p now points to the character we want to return. + * If this occurs *before* the EOB characters, then it's a + * valid NUL; if not, then we've hit the end of the buffer. + */ + if ( (yy_c_buf_p) < &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] ) + /* This was really a NUL. */ + *(yy_c_buf_p) = '\0'; + + else + { /* need more input */ + int offset = (yy_c_buf_p) - (yytext_ptr); + ++(yy_c_buf_p); + + switch ( yy_get_next_buffer( ) ) + { + case EOB_ACT_LAST_MATCH: + /* This happens because yy_g_n_b() + * sees that we've accumulated a + * token and flags that we need to + * try matching the token before + * proceeding. But for input(), + * there's no matching to consider. + * So convert the EOB_ACT_LAST_MATCH + * to EOB_ACT_END_OF_FILE. + */ + + /* Reset buffer status. */ + rtfrestart(rtfin ); + + /*FALLTHROUGH*/ + + case EOB_ACT_END_OF_FILE: + { + if ( rtfwrap( ) ) + return EOF; + + if ( ! (yy_did_buffer_switch_on_eof) ) + YY_NEW_FILE; +#ifdef __cplusplus + return yyinput(); +#else + return input(); +#endif + } + + case EOB_ACT_CONTINUE_SCAN: + (yy_c_buf_p) = (yytext_ptr) + offset; + break; + } + } + } + + c = *(unsigned char *) (yy_c_buf_p); /* cast for 8-bit char's */ + *(yy_c_buf_p) = '\0'; /* preserve rtftext */ + (yy_hold_char) = *++(yy_c_buf_p); + + return c; +} +#endif /* ifndef YY_NO_INPUT */ + +/** Immediately switch to a different input stream. + * @param input_file A readable stream. + * + * @note This function does not reset the start condition to @c INITIAL . + */ + void rtfrestart (FILE * input_file ) +{ + + if ( ! YY_CURRENT_BUFFER ){ + rtfensure_buffer_stack (); + YY_CURRENT_BUFFER_LVALUE = + rtf_create_buffer(rtfin,YY_BUF_SIZE ); + } + + rtf_init_buffer(YY_CURRENT_BUFFER,input_file ); + rtf_load_buffer_state( ); +} + +/** Switch to a different input buffer. + * @param new_buffer The new input buffer. + * + */ + void rtf_switch_to_buffer (YY_BUFFER_STATE new_buffer ) +{ + + /* TODO. We should be able to replace this entire function body + * with + * rtfpop_buffer_state(); + * rtfpush_buffer_state(new_buffer); + */ + rtfensure_buffer_stack (); + if ( YY_CURRENT_BUFFER == new_buffer ) + return; + + if ( YY_CURRENT_BUFFER ) + { + /* Flush out information for old buffer. */ + *(yy_c_buf_p) = (yy_hold_char); + YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p); + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars); + } + + YY_CURRENT_BUFFER_LVALUE = new_buffer; + rtf_load_buffer_state( ); + + /* We don't actually know whether we did this switch during + * EOF (rtfwrap()) processing, but the only time this flag + * is looked at is after rtfwrap() is called, so it's safe + * to go ahead and always set it. + */ + (yy_did_buffer_switch_on_eof) = 1; +} + +static void rtf_load_buffer_state (void) +{ + (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; + (yytext_ptr) = (yy_c_buf_p) = YY_CURRENT_BUFFER_LVALUE->yy_buf_pos; + rtfin = YY_CURRENT_BUFFER_LVALUE->yy_input_file; + (yy_hold_char) = *(yy_c_buf_p); +} + +/** Allocate and initialize an input buffer state. + * @param file A readable stream. + * @param size The character buffer size in bytes. When in doubt, use @c YY_BUF_SIZE. + * + * @return the allocated buffer state. + */ + YY_BUFFER_STATE rtf_create_buffer (FILE * file, int size ) +{ + YY_BUFFER_STATE b; + + b = (YY_BUFFER_STATE) rtfalloc(sizeof( struct yy_buffer_state ) ); + if ( ! b ) + YY_FATAL_ERROR( "out of dynamic memory in rtf_create_buffer()" ); + + b->yy_buf_size = size; + + /* yy_ch_buf has to be 2 characters longer than the size given because + * we need to put in 2 end-of-buffer characters. + */ + b->yy_ch_buf = (char *) rtfalloc(b->yy_buf_size + 2 ); + if ( ! b->yy_ch_buf ) + YY_FATAL_ERROR( "out of dynamic memory in rtf_create_buffer()" ); + + b->yy_is_our_buffer = 1; + + rtf_init_buffer(b,file ); + + return b; +} + +/** Destroy the buffer. + * @param b a buffer created with rtf_create_buffer() + * + */ + void rtf_delete_buffer (YY_BUFFER_STATE b ) +{ + + if ( ! b ) + return; + + if ( b == YY_CURRENT_BUFFER ) /* Not sure if we should pop here. */ + YY_CURRENT_BUFFER_LVALUE = (YY_BUFFER_STATE) 0; + + if ( b->yy_is_our_buffer ) + rtffree((void *) b->yy_ch_buf ); + + rtffree((void *) b ); +} + +#ifndef __cplusplus +extern int isatty (int ); +#endif /* __cplusplus */ + +/* Initializes or reinitializes a buffer. + * This function is sometimes called more than once on the same buffer, + * such as during a rtfrestart() or at EOF. + */ + static void rtf_init_buffer (YY_BUFFER_STATE b, FILE * file ) + +{ + int oerrno = errno; + + rtf_flush_buffer(b ); + + b->yy_input_file = file; + b->yy_fill_buffer = 1; + + /* If b is the current buffer, then rtf_init_buffer was _probably_ + * called from rtfrestart() or through yy_get_next_buffer. + * In that case, we don't want to reset the lineno or column. + */ + if (b != YY_CURRENT_BUFFER){ + b->yy_bs_lineno = 1; + b->yy_bs_column = 0; + } + + b->yy_is_interactive = file ? (isatty( fileno(file) ) > 0) : 0; + + errno = oerrno; +} + +/** Discard all buffered characters. On the next scan, YY_INPUT will be called. + * @param b the buffer state to be flushed, usually @c YY_CURRENT_BUFFER. + * + */ + void rtf_flush_buffer (YY_BUFFER_STATE b ) +{ + if ( ! b ) + return; + + b->yy_n_chars = 0; + + /* We always need two end-of-buffer characters. The first causes + * a transition to the end-of-buffer state. The second causes + * a jam in that state. + */ + b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR; + b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR; + + b->yy_buf_pos = &b->yy_ch_buf[0]; + + b->yy_at_bol = 1; + b->yy_buffer_status = YY_BUFFER_NEW; + + if ( b == YY_CURRENT_BUFFER ) + rtf_load_buffer_state( ); +} + +/** Pushes the new state onto the stack. The new state becomes + * the current state. This function will allocate the stack + * if necessary. + * @param new_buffer The new state. + * + */ +void rtfpush_buffer_state (YY_BUFFER_STATE new_buffer ) +{ + if (new_buffer == NULL) + return; + + rtfensure_buffer_stack(); + + /* This block is copied from rtf_switch_to_buffer. */ + if ( YY_CURRENT_BUFFER ) + { + /* Flush out information for old buffer. */ + *(yy_c_buf_p) = (yy_hold_char); + YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p); + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars); + } + + /* Only push if top exists. Otherwise, replace top. */ + if (YY_CURRENT_BUFFER) + (yy_buffer_stack_top)++; + YY_CURRENT_BUFFER_LVALUE = new_buffer; + + /* copied from rtf_switch_to_buffer. */ + rtf_load_buffer_state( ); + (yy_did_buffer_switch_on_eof) = 1; +} + +/** Removes and deletes the top of the stack, if present. + * The next element becomes the new top. + * + */ +void rtfpop_buffer_state (void) +{ + if (!YY_CURRENT_BUFFER) + return; + + rtf_delete_buffer(YY_CURRENT_BUFFER ); + YY_CURRENT_BUFFER_LVALUE = NULL; + if ((yy_buffer_stack_top) > 0) + --(yy_buffer_stack_top); + + if (YY_CURRENT_BUFFER) { + rtf_load_buffer_state( ); + (yy_did_buffer_switch_on_eof) = 1; + } +} + +/* Allocates the stack if it does not exist. + * Guarantees space for at least one push. + */ +static void rtfensure_buffer_stack (void) +{ + int num_to_alloc; + + if (!(yy_buffer_stack)) { + + /* First allocation is just for 2 elements, since we don't know if this + * scanner will even need a stack. We use 2 instead of 1 to avoid an + * immediate realloc on the next call. + */ + num_to_alloc = 1; + (yy_buffer_stack) = (struct yy_buffer_state**)rtfalloc + (num_to_alloc * sizeof(struct yy_buffer_state*) + ); + + memset((yy_buffer_stack), 0, num_to_alloc * sizeof(struct yy_buffer_state*)); + + (yy_buffer_stack_max) = num_to_alloc; + (yy_buffer_stack_top) = 0; + return; + } + + if ((yy_buffer_stack_top) >= ((yy_buffer_stack_max)) - 1){ + + /* Increase the buffer to prepare for a possible push. */ + int grow_size = 8 /* arbitrary grow size */; + + num_to_alloc = (yy_buffer_stack_max) + grow_size; + (yy_buffer_stack) = (struct yy_buffer_state**)rtfrealloc + ((yy_buffer_stack), + num_to_alloc * sizeof(struct yy_buffer_state*) + ); + + /* zero only the new slots.*/ + memset((yy_buffer_stack) + (yy_buffer_stack_max), 0, grow_size * sizeof(struct yy_buffer_state*)); + (yy_buffer_stack_max) = num_to_alloc; + } +} + +/** Setup the input buffer state to scan directly from a user-specified character buffer. + * @param base the character buffer + * @param size the size in bytes of the character buffer + * + * @return the newly allocated buffer state object. + */ +YY_BUFFER_STATE rtf_scan_buffer (char * base, yy_size_t size ) +{ + YY_BUFFER_STATE b; + + if ( size < 2 || + base[size-2] != YY_END_OF_BUFFER_CHAR || + base[size-1] != YY_END_OF_BUFFER_CHAR ) + /* They forgot to leave room for the EOB's. */ + return 0; + + b = (YY_BUFFER_STATE) rtfalloc(sizeof( struct yy_buffer_state ) ); + if ( ! b ) + YY_FATAL_ERROR( "out of dynamic memory in rtf_scan_buffer()" ); + + b->yy_buf_size = size - 2; /* "- 2" to take care of EOB's */ + b->yy_buf_pos = b->yy_ch_buf = base; + b->yy_is_our_buffer = 0; + b->yy_input_file = 0; + b->yy_n_chars = b->yy_buf_size; + b->yy_is_interactive = 0; + b->yy_at_bol = 1; + b->yy_fill_buffer = 0; + b->yy_buffer_status = YY_BUFFER_NEW; + + rtf_switch_to_buffer(b ); + + return b; +} + +/** Setup the input buffer state to scan a string. The next call to rtflex() will + * scan from a @e copy of @a str. + * @param str a NUL-terminated string to scan + * + * @return the newly allocated buffer state object. + * @note If you want to scan bytes that may contain NUL values, then use + * rtf_scan_bytes() instead. + */ +YY_BUFFER_STATE rtf_scan_string (yyconst char * yy_str ) +{ + + return rtf_scan_bytes(yy_str,strlen(yy_str) ); +} + +/** Setup the input buffer state to scan the given bytes. The next call to rtflex() will + * scan from a @e copy of @a bytes. + * @param bytes the byte buffer to scan + * @param len the number of bytes in the buffer pointed to by @a bytes. + * + * @return the newly allocated buffer state object. + */ +YY_BUFFER_STATE rtf_scan_bytes (yyconst char * bytes, int len ) +{ + YY_BUFFER_STATE b; + char *buf; + yy_size_t n; + int i; + + /* Get memory for full buffer, including space for trailing EOB's. */ + n = len + 2; + buf = (char *) rtfalloc(n ); + if ( ! buf ) + YY_FATAL_ERROR( "out of dynamic memory in rtf_scan_bytes()" ); + + for ( i = 0; i < len; ++i ) + buf[i] = bytes[i]; + + buf[len] = buf[len+1] = YY_END_OF_BUFFER_CHAR; + + b = rtf_scan_buffer(buf,n ); + if ( ! b ) + YY_FATAL_ERROR( "bad buffer in rtf_scan_bytes()" ); + + /* It's okay to grow etc. this buffer, and we should throw it + * away when we're done. + */ + b->yy_is_our_buffer = 1; + + return b; +} + +#ifndef YY_EXIT_FAILURE +#define YY_EXIT_FAILURE 2 +#endif + +static void yy_fatal_error (yyconst char* msg ) +{ + (void) fprintf( stderr, "%s\n", msg ); + exit( YY_EXIT_FAILURE ); +} + +/* Redefine yyless() so it works in section 3 code. */ + +#undef yyless +#define yyless(n) \ + do \ + { \ + /* Undo effects of setting up rtftext. */ \ + int yyless_macro_arg = (n); \ + YY_LESS_LINENO(yyless_macro_arg);\ + rtftext[rtfleng] = (yy_hold_char); \ + (yy_c_buf_p) = rtftext + yyless_macro_arg; \ + (yy_hold_char) = *(yy_c_buf_p); \ + *(yy_c_buf_p) = '\0'; \ + rtfleng = yyless_macro_arg; \ + } \ + while ( 0 ) + +/* Accessor methods (get/set functions) to struct members. */ + +/** Get the current line number. + * + */ +int rtfget_lineno (void) +{ + + return rtflineno; +} + +/** Get the input stream. + * + */ +FILE *rtfget_in (void) +{ + return rtfin; +} + +/** Get the output stream. + * + */ +FILE *rtfget_out (void) +{ + return rtfout; +} + +/** Get the length of the current token. + * + */ +int rtfget_leng (void) +{ + return rtfleng; +} + +/** Get the current token. + * + */ + +char *rtfget_text (void) +{ + return rtftext; +} + +/** Set the current line number. + * @param line_number + * + */ +void rtfset_lineno (int line_number ) +{ + + rtflineno = line_number; +} + +/** Set the input stream. This does not discard the current + * input buffer. + * @param in_str A readable stream. + * + * @see rtf_switch_to_buffer + */ +void rtfset_in (FILE * in_str ) +{ + rtfin = in_str ; +} + +void rtfset_out (FILE * out_str ) +{ + rtfout = out_str ; +} + +int rtfget_debug (void) +{ + return rtf_flex_debug; +} + +void rtfset_debug (int bdebug ) +{ + rtf_flex_debug = bdebug ; +} + +/* rtflex_destroy is for both reentrant and non-reentrant scanners. */ +int rtflex_destroy (void) +{ + + /* Pop the buffer stack, destroying each element. */ + while(YY_CURRENT_BUFFER){ + rtf_delete_buffer(YY_CURRENT_BUFFER ); + YY_CURRENT_BUFFER_LVALUE = NULL; + rtfpop_buffer_state(); + } + + /* Destroy the stack itself. */ + rtffree((yy_buffer_stack) ); + (yy_buffer_stack) = NULL; + + return 0; +} + +/* + * Internal utility routines. + */ + +#ifndef yytext_ptr +static void yy_flex_strncpy (char* s1, yyconst char * s2, int n ) +{ + register int i; + for ( i = 0; i < n; ++i ) + s1[i] = s2[i]; +} +#endif + +#ifdef YY_NEED_STRLEN +static int yy_flex_strlen (yyconst char * s ) +{ + register int n; + for ( n = 0; s[n]; ++n ) + ; + + return n; +} +#endif + +void *rtfalloc (yy_size_t size ) +{ + return (void *) malloc( size ); +} + +void *rtfrealloc (void * ptr, yy_size_t size ) +{ + /* The cast to (char *) in the following accommodates both + * implementations that use char* generic pointers, and those + * that use void* generic pointers. It works with the latter + * because both ANSI C and C++ allow castless assignment from + * any pointer type to void*, and deal with argument conversions + * as though doing an assignment. + */ + return (void *) realloc( (char *) ptr, size ); +} + +void rtffree (void * ptr ) +{ + free( (char *) ptr ); /* see rtfrealloc() for (char *) cast */ +} + +#define YYTABLES_NAME "yytables" + +#undef YY_NEW_FILE +#undef YY_FLUSH_BUFFER +#undef yy_set_bol +#undef yy_new_buffer +#undef yy_set_interactive +#undef yytext_ptr +#undef YY_DO_BEFORE_ACTION + +#ifdef YY_DECL_IS_OURS +#undef YY_DECL_IS_OURS +#undef YY_DECL +#endif +#line 57 "rtf.ll" + + + +#include "rtf2html.h" + +void ParStyle::clearFormatting() +{ + // For now, do nothing. + // dir is not a formatting item. +} + +QString RTF2HTML::quoteString(const QString &_str, quoteMode mode) +{ + QString str = _str; + str.replace(QRegExp("&"), "&"); + str.replace(QRegExp("<"), "<"); + str.replace(QRegExp(">"), ">"); + str.replace(QRegExp("\""), """); + str.replace(QRegExp("\r"), ""); + switch (mode){ + case quoteHTML: + str.replace(QRegExp("\n"), "<br>\n"); + break; + case quoteXML: + str.replace(QRegExp("\n"), "<br/>\n"); + break; + default: + break; + } + QRegExp re(" +"); + int len; + int pos = 0; + + while ((pos = re.search(str, pos)) != -1) { + len = re.matchedLength(); + + if (len == 1) + continue; + QString s = " "; + for (int i = 1; i < len; i++) + s += " "; + str.replace(pos, len, s); + } + return str; +} + +RTF2HTML::RTF2HTML() + : cur_level(this) +{ + rtf_ptr = NULL; + bExplicitParagraph = false; +} + +OutTag* RTF2HTML::getTopOutTag(TagEnum tagType) +{ + vector<OutTag>::iterator it, it_end; + for(it = oTags.begin(), it_end = oTags.end(); it != it_end; ++it) + if (it->tag == tagType) + return &(*it); + return NULL; +} + +void RTF2HTML::FlushOutTags() +{ + vector<OutTag>::iterator iter; + for (iter = oTags.begin(); iter != oTags.end(); iter++) + { + OutTag &t = *iter; + switch (t.tag){ + case TAG_FONT_COLOR: + { + // RTF colors are 1-based; colors[] is a 0-based array. + if (t.param > colors.size() || t.param == 0) + break; + QColor &c = colors[t.param-1]; + PrintUnquoted("<span style=\"color:#%02X%02X%02X\">", c.red(), c.green(), c.blue()); + } + break; + case TAG_FONT_SIZE: + PrintUnquoted("<span style=\"font-size:%upt\">", t.param); + break; + case TAG_FONT_FAMILY: + { + if (t.param > fonts.size() || t.param == 0) + break; + FontDef &f = fonts[t.param-1]; + string name = (!f.nonTaggedName.empty()) ? f.nonTaggedName : f.taggedName; + PrintUnquoted("<span style=\"font-family:%s\">", name.c_str()); + } + break; + case TAG_BG_COLOR:{ + if (t.param > colors.size() || t.param == 0) + break; + QColor &c = colors[t.param-1]; + PrintUnquoted("<span style=\"background-color:#%02X%02X%02X;\">", c.red(), c.green(), c.blue()); + break; + } + case TAG_BOLD: + PrintUnquoted("<b>"); + break; + case TAG_ITALIC: + PrintUnquoted("<i>"); + break; + case TAG_UNDERLINE: + PrintUnquoted("<u>"); + break; + default: + break; + } + } + oTags.clear(); +} + +// This function will close the already-opened tag 'tag'. It will take +// care of closing the tags which 'tag' contains first (ie. it will unroll +// the stack till the point where 'tag' is). +void Level::resetTag(TagEnum tag) +{ + // A stack which'll keep tags we had to close in order to reach 'tag'. + // After we close 'tag', we will reopen them. + stack<TagEnum> s; + + while (p->tags.size() > m_nTagsStartPos){ // Don't go further than the point where this level starts. + + TagEnum nTag = p->tags.top(); + + /* A tag will be located in oTags if it still wasn't printed out. + A tag will get printed out only if necessary (e.g. <I></I> will + be optimized away). + Thus, for each tag we remove from the actual tag stack, we also + try to remove a yet-to-be-printed tag, and only if there are no + yet-to-be-printed tags left, we start closing the tags we pop. + The tags have one space - needed for umlaute (�) and .utf8() + */ + if (p->oTags.empty()){ + switch (nTag){ + case TAG_FONT_COLOR: + case TAG_FONT_SIZE: + case TAG_BG_COLOR: + case TAG_FONT_FAMILY: + p->PrintUnquoted(" </span>"); + break; + case TAG_BOLD: + p->PrintUnquoted(" </b>"); + break; + case TAG_ITALIC: + p->PrintUnquoted(" </i>"); + break; + case TAG_UNDERLINE: + p->PrintUnquoted(" </u>"); + break; + default: + break; + } + }else{ + p->oTags.pop_back(); + } + + p->tags.pop(); + if (nTag == tag) break; // if we reached the tag we were looking to close. + s.push(nTag); // remember to reopen this tag + } + + if (tag == TAG_ALL) return; + + while (!s.empty()){ + TagEnum nTag = s.top(); + switch (nTag){ + case TAG_FONT_COLOR:{ + unsigned nFontColor = m_nFontColor; + m_nFontColor = 0; + setFontColor(nFontColor); + break; + } + case TAG_FONT_SIZE:{ + unsigned nFontSize = m_nFontSize; + m_nFontSize = 0; + setFontSize(nFontSize); + break; + } + case TAG_BG_COLOR:{ + unsigned nFontBgColor = m_nFontBgColor; + m_nFontBgColor = 0; + setFontBgColor(nFontBgColor); + break; + } + case TAG_FONT_FAMILY:{ + unsigned nFont = m_nFont; + m_nFont = 0; + setFont(nFont); + break; + } + case TAG_BOLD:{ + bool nBold = m_bBold; + m_bBold = false; + setBold(nBold); + break; + } + case TAG_ITALIC:{ + bool nItalic = m_bItalic; + m_bItalic = false; + setItalic(nItalic); + break; + } + case TAG_UNDERLINE:{ + bool nUnderline = m_bUnderline; + m_bUnderline = false; + setUnderline(nUnderline); + break; + } + default: + break; + } + s.pop(); + } +} + +Level::Level(RTF2HTML *_p) : + p(_p), + m_bFontTbl(false), + m_bColors(false), + m_bFontName(false), + m_bTaggedFontNameOk(false), + m_nFont(0), + m_nEncoding(0) +{ + m_nTagsStartPos = p->tags.size(); + Init(); +} + +Level::Level(const Level &l) : + p(l.p), + m_bFontTbl(l.m_bFontTbl), + m_bColors(l.m_bColors), + m_bFontName(false), + m_bTaggedFontNameOk(l.m_bTaggedFontNameOk), + m_nFont(l.m_nFont), + m_nEncoding(l.m_nEncoding) +{ + m_nTagsStartPos = p->tags.size(); + Init(); +} + +void Level::Init() +{ + m_nFontColor = 0; + m_nFontBgColor = 0; + m_nFontSize = 0; + m_bFontName = false; + m_bBold = false; + m_bItalic = false; + m_bUnderline = false; +} + +void RTF2HTML::PrintUnquoted(const char *str, ...) +{ + char buff[1024]; + va_list ap; + va_start(ap, str); + vsnprintf(buff, sizeof(buff), str, ap); + va_end(ap); + sParagraph += buff; +} + +void RTF2HTML::PrintQuoted(const QString &str) +{ + sParagraph += quoteString(str); +} + +void RTF2HTML::FlushParagraph() +{ + if (!bExplicitParagraph || sParagraph.isEmpty()) + return; + + /* + s += "<p dir=\""; + // Note: Lower-case 'ltr' and 'rtl' are important for Qt. + s += (parStyle.dir == ParStyle::DirRTL ? "rtl" : "ltr"); + s += "\">"; + s += sParagraph; + s += "</p>"; + */ + + s += sParagraph; + s += "<br>"; + + // Clear up the paragraph members + sParagraph = ""; + bExplicitParagraph = false; +} + +void Level::setFont(unsigned nFont) +{ + if (nFont <= 0) + return; + + if (m_bFontTbl){ + if (nFont > p->fonts.size() +1){ + kdDebug(14200) << "Invalid font index (" << + nFont << ") while parsing font table." << endl; + return; + } + if (nFont > p->fonts.size()){ + FontDef f; + f.charset = 0; + p->fonts.push_back(f); + } + m_nFont = nFont; + } + else + { + if (nFont > p->fonts.size()) + { + kdDebug(14200) << "Invalid font index (" << + nFont << ")." << endl; + return; + } + if (m_nFont == nFont) + return; + m_nFont = nFont; + if (m_nFont) resetTag(TAG_FONT_FAMILY); + m_nEncoding = p->fonts[nFont-1].charset; + p->oTags.push_back(OutTag(TAG_FONT_FAMILY, nFont)); + p->PutTag(TAG_FONT_FAMILY); + } +} + +void Level::setFontName() +{ + // This function is only valid during font table parsing. + if (m_bFontTbl){ + if ((m_nFont > 0) && (m_nFont <= p->fonts.size())) + // Be prepared to accept a font name. + m_bFontName = true; + } +} + +void Level::setEncoding(unsigned nEncoding) +{ + if (m_bFontTbl){ + if ((m_nFont > 0) && (m_nFont <= p->fonts.size())) + p->fonts[m_nFont-1].charset = nEncoding; + return; + } + m_nEncoding = nEncoding; +} + +void Level::setBold(bool bBold) +{ + if (m_bBold == bBold) return; + if (m_bBold) resetTag(TAG_BOLD); + m_bBold = bBold; + if (!m_bBold) return; + p->oTags.push_back(OutTag(TAG_BOLD, 0)); + p->PutTag(TAG_BOLD); +} + +void Level::setItalic(bool bItalic) +{ + if (m_bItalic == bItalic) return; + if (m_bItalic) resetTag(TAG_ITALIC); + m_bItalic = bItalic; + if (!m_bItalic) return; + p->oTags.push_back(OutTag(TAG_ITALIC, 0)); + p->PutTag(TAG_ITALIC); +} + +void Level::setUnderline(bool bUnderline) +{ + if (m_bUnderline == bUnderline) return; + if (m_bUnderline) resetTag(TAG_UNDERLINE); + m_bUnderline = bUnderline; + if (!m_bUnderline) return; + p->oTags.push_back(OutTag(TAG_UNDERLINE, 0)); + p->PutTag(TAG_UNDERLINE); +} + +void Level::setFontColor(unsigned short nColor) +{ + if (m_nFontColor == nColor) return; + if (m_nFontColor) resetTag(TAG_FONT_COLOR); + if (nColor > p->colors.size()) return; + m_nFontColor = nColor; + p->oTags.push_back(OutTag(TAG_FONT_COLOR, m_nFontColor)); + p->PutTag(TAG_FONT_COLOR); +} + +void Level::setFontBgColor(unsigned short nColor) +{ + if (m_nFontBgColor == nColor) return; + if (m_nFontBgColor != 0) resetTag(TAG_BG_COLOR); + if (nColor > p->colors.size()) return; + m_nFontBgColor = nColor; + p->oTags.push_back(OutTag(TAG_BG_COLOR, m_nFontBgColor)); + p->PutTag(TAG_BG_COLOR); +} + +void Level::setFontSizeHalfPoints(unsigned short nSize) +{ + setFontSize(nSize / 2); +} + +void Level::setFontSize(unsigned short nSize) +{ + if (m_nFontSize == nSize) return; + if (m_nFontSize) resetTag(TAG_FONT_SIZE); + p->oTags.push_back(OutTag(TAG_FONT_SIZE, nSize)); + p->PutTag(TAG_FONT_SIZE); + m_nFontSize = nSize; +} + +void Level::startParagraph() +{ + // Whatever tags we have open now, close them. + // We cannot carry let character formatting tags wrap paragraphs, + // since a formatting tag can close at any time and we cannot + // close the paragraph any time we want. + resetTag(TAG_ALL); + + // Flush the current paragraph HTML to the document HTML. + p->FlushParagraph(); + + // Mark this new paragraph as an explicit one (from \par etc.). + p->bExplicitParagraph = true; + + // Restore character formatting + p->oTags.push_back(OutTag(TAG_FONT_SIZE, m_nFontSize)); + p->PutTag(TAG_FONT_SIZE); + p->oTags.push_back(OutTag(TAG_FONT_COLOR, m_nFontColor)); + p->PutTag(TAG_FONT_COLOR); + p->oTags.push_back(OutTag(TAG_FONT_FAMILY, m_nFont)); + p->PutTag(TAG_FONT_FAMILY); + if (m_nFontBgColor != 0) + { + p->oTags.push_back(OutTag(TAG_BG_COLOR, m_nFontBgColor)); + p->PutTag(TAG_BG_COLOR); + } + if (m_bBold) + { + p->oTags.push_back(OutTag(TAG_BOLD, 0)); + p->PutTag(TAG_BOLD); + } + if (m_bItalic) + { + p->PutTag(TAG_ITALIC); + p->oTags.push_back(OutTag(TAG_ITALIC, 0)); + } + if (m_bUnderline) + { + p->oTags.push_back(OutTag(TAG_UNDERLINE, 0)); + p->PutTag(TAG_UNDERLINE); + } +} + +bool Level::isParagraphOpen() const +{ + return p->bExplicitParagraph; +} + +void Level::clearParagraphFormatting() +{ + // implicitly start a paragraph + if (!isParagraphOpen()) + startParagraph(); + // Since we don't implement any of the paragraph formatting tags (e.g. alignment), + // we don't clean up anything here. Note that \pard does NOT clean character + // formatting (such as font size, font weight, italics...). + p->parStyle.clearFormatting(); +} + +void Level::setParagraphDirLTR() +{ + // implicitly start a paragraph + if (!isParagraphOpen()) + startParagraph(); + p->parStyle.dir = ParStyle::DirLTR; +} + +void Level::setParagraphDirRTL() +{ + // implicitly start a paragraph + if (!isParagraphOpen()) + startParagraph(); + p->parStyle.dir = ParStyle::DirRTL; +} + +void Level::addLineBreak() +{ + p->PrintUnquoted("<br/>"); +} + +void Level::reset() +{ + resetTag(TAG_ALL); + if (m_bColors){ + if (m_bColorInit){ + QColor c(m_nRed, m_nGreen, m_nBlue); + p->colors.push_back(c); + resetColors(); + } + return; + } +} + +void Level::setText(const char *str) +{ + if (m_bColors) + { + reset(); + } + else if (m_bFontTbl) + { + if ((m_nFont <= 0) || (m_nFont > p->fonts.size())) + return; + + FontDef& def = p->fonts[m_nFont-1]; + + char *pp = strchr(str, ';'); + unsigned size; + if (pp != NULL) + size = (pp - str); + else + size = strlen(str); + + if (m_bFontName) + { + def.nonTaggedName.append(str, size); + // We know we have the entire name + if (pp != NULL) + m_bFontName = false; + } + else if (!m_bTaggedFontNameOk) + { + def.taggedName.append(str, size); + if (pp != NULL) + m_bTaggedFontNameOk = true; + } + } + else + { + for (; *str; str++) + if ((unsigned char)(*str) >= ' ') break; + if (!*str) return; + p->FlushOutTags(); + text += str; + } +} + +void Level::flush() +{ + if (text.length() == 0) return; + // TODO: Make encoding work in Kopete + /* + const char *encoding = NULL; + if (m_nEncoding){ + for (const ENCODING *c = ICQPlugin::core->encodings; c->language; c++){ + if (!c->bMain) + continue; + if ((unsigned)c->rtf_code == m_nEncoding){ + encoding = c->codec; + break; + } + } + } + if (encoding == NULL) + encoding = p->encoding; + + QTextCodec *codec = ICQClient::_getCodec(encoding); + */ + //p->PrintQuoted(codec->toUnicode(text.c_str(), text.length())); + p->PrintQuoted(text.c_str()); + text = ""; +} + +const unsigned FONTTBL = 0; +const unsigned COLORTBL = 1; +const unsigned RED = 2; +const unsigned GREEN = 3; +const unsigned BLUE = 4; +const unsigned CF = 5; +const unsigned FS = 6; +const unsigned HIGHLIGHT = 7; +const unsigned PARD = 8; +const unsigned PAR = 9; +const unsigned I = 10; +const unsigned B = 11; +const unsigned UL = 12; +const unsigned F = 13; +const unsigned FCHARSET = 14; +const unsigned FNAME = 15; +const unsigned ULNONE = 16; +const unsigned LTRPAR = 17; +const unsigned RTLPAR = 18; +const unsigned LINE = 19; + +static char cmds[] = + "fonttbl\x00" + "colortbl\x00" + "red\x00" + "green\x00" + "blue\x00" + "cf\x00" + "fs\x00" + "highlight\x00" + "pard\x00" + "par\x00" + "i\x00" + "b\x00" + "ul\x00" + "f\x00" + "fcharset\x00" + "fname\x00" + "ulnone\x00" + "ltrpar\x00" + "rtlpar\x00" + "line\x00" + "\x00"; + +int rtfwrap() { return 1; } + +static char h2d(char c) +{ + if ((c >= '0') && (c <= '9')) + return c - '0'; + if ((c >= 'A') && (c <= 'F')) + return (c - 'A') + 10; + if ((c >= 'a') && (c <= 'f')) + return (c - 'a') + 10; + return 0; +} + +QString RTF2HTML::Parse(const char *rtf, const char *_encoding) +{ + encoding = _encoding; + YY_BUFFER_STATE yy_current_buffer = rtf_scan_string(rtf); + rtf_ptr = rtf; + for (;;){ + int res = rtflex(); + if (!res) break; + switch (res){ + case UP:{ + cur_level.flush(); + levels.push(cur_level); + break; + } + case DOWN:{ + if (!levels.empty()){ + cur_level.flush(); + cur_level.reset(); + cur_level = levels.top(); + levels.pop(); + } + break; + } + case IMG:{ + cur_level.flush(); + const char ICQIMAGE[] = "icqimage"; + const char *smiles[] = { ":-)" , ":-O" , ":-|" , ":-/" , // 0-3 + ":-(" , ":-*" , ":-/" , ":'(" , // 4-7 + ";-)" , ":-@" , ":-$" , ":-X" , // 8-B + ":-P" , "8-)" , "O:)" , ":-D" }; // C-F + const char *p = rtftext + 3; + if ((strlen(p) > strlen(ICQIMAGE)) && !memcmp(p, ICQIMAGE, strlen(ICQIMAGE))){ + unsigned n = 0; + for (p += strlen(ICQIMAGE); *p; p++){ + if ((*p >= '0') && (*p <= '9')){ + n = n << 4; + n += (*p - '0'); + continue; + } + if ((*p >= 'A') && (*p <= 'F')){ + n = n << 4; + n += (*p - 'A') + 10; + continue; + } + if ((*p >= 'a') && (*p <= 'f')){ + n = n << 4; + n += (*p - 'a') + 10; + continue; + } + break; + } + if (n < 16) + PrintUnquoted(" %s ", smiles[n] ); + }else{ + kdDebug(14200) << "Unknown image " << rtftext << endl; + } + break; + } + case SKIP: + break; + case SLASH: + cur_level.setText(rtftext+1); + break; + case TXT: + cur_level.setText(rtftext); + break; + case UNICODE_CHAR:{ + cur_level.flush(); + sParagraph += QChar((unsigned short)(atol(rtftext + 2))); + break; + } + case HEX:{ + char s[2]; + s[0] = (h2d(rtftext[2]) << 4) + h2d(rtftext[3]); + s[1] = 0; + cur_level.setText(s); + break; + } + case CMD: + { + cur_level.flush(); + const char *cmd = rtftext + 1; + unsigned n_cmd = 0; + unsigned cmd_size = 0; + int cmd_value = -1; + const char *p; + for (p = cmd; *p; p++, cmd_size++) + if (((*p >= '0') && (*p <= '9')) || (*p == ' ')) break; + if (*p && (*p != ' ')) cmd_value = atol(p); + for (p = cmds; *p; p += strlen(p) + 1, n_cmd++){ + if (strlen(p) > cmd_size) continue; + if (!memcmp(p, cmd, cmd_size)) break; + } + cmd += strlen(p); + switch (n_cmd){ + case FONTTBL: // fonttbl + cur_level.setFontTbl(); + break; + case COLORTBL: + cur_level.setColors(); + break; + case RED: + cur_level.setRed(cmd_value); + break; + case GREEN: + cur_level.setGreen(cmd_value); + break; + case BLUE: + cur_level.setBlue(cmd_value); + break; + case CF: + cur_level.setFontColor(cmd_value); + break; + case FS: + cur_level.setFontSizeHalfPoints(cmd_value); + break; + case HIGHLIGHT: + cur_level.setFontBgColor(cmd_value); + break; + case PARD: + cur_level.clearParagraphFormatting(); + break; + case PAR: + cur_level.startParagraph(); + break; + case I: + cur_level.setItalic(cmd_value != 0); + break; + case B: + cur_level.setBold(cmd_value != 0); + break; + case UL: + cur_level.setUnderline(cmd_value != 0); + break; + case ULNONE: + cur_level.setUnderline(false); + break; + case F: + // RTF fonts are 0-based; our font index is 1-based. + cur_level.setFont(cmd_value+1); + break; + case FCHARSET: + cur_level.setEncoding(cmd_value); + break; + case FNAME: + cur_level.setFontName(); + break; + case LTRPAR: + cur_level.setParagraphDirLTR(); + break; + case RTLPAR: + cur_level.setParagraphDirRTL(); + break; + case LINE: + cur_level.addLineBreak(); + } + break; + } + } + } + rtf_delete_buffer(yy_current_buffer); + yy_current_buffer = NULL; + FlushParagraph(); + return s; +} + +/* +bool ICQClient::parseRTF(const char *rtf, const char *encoding, QString &res) +{ + char _RTF[] = "{\\rtf"; + if ((strlen(rtf) > strlen(_RTF)) && !memcmp(rtf, _RTF, strlen(_RTF))){ + RTF2HTML p; + res = p.Parse(rtf, encoding); + return true; + } + QTextCodec *codec = ICQClient::_getCodec(encoding); + res = codec->toUnicode(rtf, strlen(rtf)); + return false; +} +*/ + diff --git a/kopete/protocols/groupwise/libgroupwise/rtf.ll b/kopete/protocols/groupwise/libgroupwise/rtf.ll new file mode 100644 index 00000000..37ebd9a3 --- /dev/null +++ b/kopete/protocols/groupwise/libgroupwise/rtf.ll @@ -0,0 +1,866 @@ +%{ +/* + rtf.ll - A simple RTF Parser (Flex code) + + Copyright (c) 2002 by Vladimir Shutoff <vovan@shutoff.ru> (original code) + Copyright (c) 2004 by Thiago S. Barcelos <barcelos@ime.usp.br> (Kopete port) + Kopete (c) 2002-2003 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ************************************************************************* + +update rtf.cc: +flex -olex.yy.c `test -f rtf.ll || echo './'`rtf.ll +sed '/^#/ s|lex.yy\.c|rtf.cc|' lex.yy.c >rtf.cc +rm -f lex.yy.c + +*/ + +#define UP 1 +#define DOWN 2 +#define CMD 3 +#define TXT 4 +#define HEX 5 +#define IMG 6 +#define UNICODE_CHAR 7 +#define SKIP 8 +#define SLASH 9 +#define S_TXT 10 + +#define YY_NEVER_INTERACTIVE 1 +#define YY_ALWAYS_INTERACTIVE 0 +#define YY_MAIN 0 + +%} + +%option nounput +%option nostack +%option prefix="rtf" + +%% + +"{" { return UP; } +"}" { return DOWN; } +"\\"[\\\{\}] { return SLASH; } +"\\u"[0-9]{3,7}[ ]?"?" { return UNICODE_CHAR; } +"\\"[A-Za-z]+[0-9]*[ ]? { return CMD; } +"\\'"[0-9A-Fa-f][0-9A-Fa-f] { return HEX; } +"<##"[^>]+">" { return IMG; } +[^\\{}<]+ { return TXT; } +. { return TXT; } +%% + +#include "rtf2html.h" + +void ParStyle::clearFormatting() +{ + // For now, do nothing. + // dir is not a formatting item. +} + +QString RTF2HTML::quoteString(const QString &_str, quoteMode mode) +{ + QString str = _str; + str.replace(QRegExp("&"), "&"); + str.replace(QRegExp("<"), "<"); + str.replace(QRegExp(">"), ">"); + str.replace(QRegExp("\""), """); + str.replace(QRegExp("\r"), ""); + switch (mode){ + case quoteHTML: + str.replace(QRegExp("\n"), "<br>\n"); + break; + case quoteXML: + str.replace(QRegExp("\n"), "<br/>\n"); + break; + default: + break; + } + QRegExp re(" +"); + int len; + int pos = 0; + + while ((pos = re.search(str, pos)) != -1) { + len = re.matchedLength(); + + if (len == 1) + continue; + QString s = " "; + for (int i = 1; i < len; i++) + s += " "; + str.replace(pos, len, s); + } + return str; +} + +RTF2HTML::RTF2HTML() + : cur_level(this) +{ + rtf_ptr = NULL; + bExplicitParagraph = false; +} + +OutTag* RTF2HTML::getTopOutTag(TagEnum tagType) +{ + vector<OutTag>::iterator it, it_end; + for(it = oTags.begin(), it_end = oTags.end(); it != it_end; ++it) + if (it->tag == tagType) + return &(*it); + return NULL; +} + +void RTF2HTML::FlushOutTags() +{ + vector<OutTag>::iterator iter; + for (iter = oTags.begin(); iter != oTags.end(); iter++) + { + OutTag &t = *iter; + switch (t.tag){ + case TAG_FONT_COLOR: + { + // RTF colors are 1-based; colors[] is a 0-based array. + if (t.param > colors.size() || t.param == 0) + break; + QColor &c = colors[t.param-1]; + PrintUnquoted("<span style=\"color:#%02X%02X%02X\">", c.red(), c.green(), c.blue()); + } + break; + case TAG_FONT_SIZE: + PrintUnquoted("<span style=\"font-size:%upt\">", t.param); + break; + case TAG_FONT_FAMILY: + { + if (t.param > fonts.size() || t.param == 0) + break; + FontDef &f = fonts[t.param-1]; + string name = (!f.nonTaggedName.empty()) ? f.nonTaggedName : f.taggedName; + PrintUnquoted("<span style=\"font-family:%s\">", name.c_str()); + } + break; + case TAG_BG_COLOR:{ + if (t.param > colors.size() || t.param == 0) + break; + QColor &c = colors[t.param-1]; + PrintUnquoted("<span style=\"background-color:#%02X%02X%02X;\">", c.red(), c.green(), c.blue()); + break; + } + case TAG_BOLD: + PrintUnquoted("<b>"); + break; + case TAG_ITALIC: + PrintUnquoted("<i>"); + break; + case TAG_UNDERLINE: + PrintUnquoted("<u>"); + break; + default: + break; + } + } + oTags.clear(); +} + +// This function will close the already-opened tag 'tag'. It will take +// care of closing the tags which 'tag' contains first (ie. it will unroll +// the stack till the point where 'tag' is). +void Level::resetTag(TagEnum tag) +{ + // A stack which'll keep tags we had to close in order to reach 'tag'. + // After we close 'tag', we will reopen them. + stack<TagEnum> s; + + while (p->tags.size() > m_nTagsStartPos){ // Don't go further than the point where this level starts. + + TagEnum nTag = p->tags.top(); + + /* A tag will be located in oTags if it still wasn't printed out. + A tag will get printed out only if necessary (e.g. <I></I> will + be optimized away). + Thus, for each tag we remove from the actual tag stack, we also + try to remove a yet-to-be-printed tag, and only if there are no + yet-to-be-printed tags left, we start closing the tags we pop. + The tags have one space - needed for umlaute (�) and .utf8() + */ + if (p->oTags.empty()){ + switch (nTag){ + case TAG_FONT_COLOR: + case TAG_FONT_SIZE: + case TAG_BG_COLOR: + case TAG_FONT_FAMILY: + p->PrintUnquoted(" </span>"); + break; + case TAG_BOLD: + p->PrintUnquoted(" </b>"); + break; + case TAG_ITALIC: + p->PrintUnquoted(" </i>"); + break; + case TAG_UNDERLINE: + p->PrintUnquoted(" </u>"); + break; + default: + break; + } + }else{ + p->oTags.pop_back(); + } + + p->tags.pop(); + if (nTag == tag) break; // if we reached the tag we were looking to close. + s.push(nTag); // remember to reopen this tag + } + + if (tag == TAG_ALL) return; + + while (!s.empty()){ + TagEnum nTag = s.top(); + switch (nTag){ + case TAG_FONT_COLOR:{ + unsigned nFontColor = m_nFontColor; + m_nFontColor = 0; + setFontColor(nFontColor); + break; + } + case TAG_FONT_SIZE:{ + unsigned nFontSize = m_nFontSize; + m_nFontSize = 0; + setFontSize(nFontSize); + break; + } + case TAG_BG_COLOR:{ + unsigned nFontBgColor = m_nFontBgColor; + m_nFontBgColor = 0; + setFontBgColor(nFontBgColor); + break; + } + case TAG_FONT_FAMILY:{ + unsigned nFont = m_nFont; + m_nFont = 0; + setFont(nFont); + break; + } + case TAG_BOLD:{ + bool nBold = m_bBold; + m_bBold = false; + setBold(nBold); + break; + } + case TAG_ITALIC:{ + bool nItalic = m_bItalic; + m_bItalic = false; + setItalic(nItalic); + break; + } + case TAG_UNDERLINE:{ + bool nUnderline = m_bUnderline; + m_bUnderline = false; + setUnderline(nUnderline); + break; + } + default: + break; + } + s.pop(); + } +} + +Level::Level(RTF2HTML *_p) : + p(_p), + m_bFontTbl(false), + m_bColors(false), + m_bFontName(false), + m_bTaggedFontNameOk(false), + m_nFont(0), + m_nEncoding(0) +{ + m_nTagsStartPos = p->tags.size(); + Init(); +} + +Level::Level(const Level &l) : + p(l.p), + m_bFontTbl(l.m_bFontTbl), + m_bColors(l.m_bColors), + m_bFontName(false), + m_bTaggedFontNameOk(l.m_bTaggedFontNameOk), + m_nFont(l.m_nFont), + m_nEncoding(l.m_nEncoding) +{ + m_nTagsStartPos = p->tags.size(); + Init(); +} + +void Level::Init() +{ + m_nFontColor = 0; + m_nFontBgColor = 0; + m_nFontSize = 0; + m_bFontName = false; + m_bBold = false; + m_bItalic = false; + m_bUnderline = false; +} + +void RTF2HTML::PrintUnquoted(const char *str, ...) +{ + char buff[1024]; + va_list ap; + va_start(ap, str); + vsnprintf(buff, sizeof(buff), str, ap); + va_end(ap); + sParagraph += buff; +} + +void RTF2HTML::PrintQuoted(const QString &str) +{ + sParagraph += quoteString(str); +} + +void RTF2HTML::FlushParagraph() +{ + if (!bExplicitParagraph || sParagraph.isEmpty()) + return; + + /* + s += "<p dir=\""; + // Note: Lower-case 'ltr' and 'rtl' are important for Qt. + s += (parStyle.dir == ParStyle::DirRTL ? "rtl" : "ltr"); + s += "\">"; + s += sParagraph; + s += "</p>"; + */ + + s += sParagraph; + s += "<br>"; + + // Clear up the paragraph members + sParagraph = ""; + bExplicitParagraph = false; +} + +void Level::setFont(unsigned nFont) +{ + if (nFont <= 0) + return; + + if (m_bFontTbl){ + if (nFont > p->fonts.size() +1){ + kdDebug(14200) << "Invalid font index (" << + nFont << ") while parsing font table." << endl; + return; + } + if (nFont > p->fonts.size()){ + FontDef f; + f.charset = 0; + p->fonts.push_back(f); + } + m_nFont = nFont; + } + else + { + if (nFont > p->fonts.size()) + { + kdDebug(14200) << "Invalid font index (" << + nFont << ")." << endl; + return; + } + if (m_nFont == nFont) + return; + m_nFont = nFont; + if (m_nFont) resetTag(TAG_FONT_FAMILY); + m_nEncoding = p->fonts[nFont-1].charset; + p->oTags.push_back(OutTag(TAG_FONT_FAMILY, nFont)); + p->PutTag(TAG_FONT_FAMILY); + } +} + +void Level::setFontName() +{ + // This function is only valid during font table parsing. + if (m_bFontTbl){ + if ((m_nFont > 0) && (m_nFont <= p->fonts.size())) + // Be prepared to accept a font name. + m_bFontName = true; + } +} + +void Level::setEncoding(unsigned nEncoding) +{ + if (m_bFontTbl){ + if ((m_nFont > 0) && (m_nFont <= p->fonts.size())) + p->fonts[m_nFont-1].charset = nEncoding; + return; + } + m_nEncoding = nEncoding; +} + +void Level::setBold(bool bBold) +{ + if (m_bBold == bBold) return; + if (m_bBold) resetTag(TAG_BOLD); + m_bBold = bBold; + if (!m_bBold) return; + p->oTags.push_back(OutTag(TAG_BOLD, 0)); + p->PutTag(TAG_BOLD); +} + +void Level::setItalic(bool bItalic) +{ + if (m_bItalic == bItalic) return; + if (m_bItalic) resetTag(TAG_ITALIC); + m_bItalic = bItalic; + if (!m_bItalic) return; + p->oTags.push_back(OutTag(TAG_ITALIC, 0)); + p->PutTag(TAG_ITALIC); +} + +void Level::setUnderline(bool bUnderline) +{ + if (m_bUnderline == bUnderline) return; + if (m_bUnderline) resetTag(TAG_UNDERLINE); + m_bUnderline = bUnderline; + if (!m_bUnderline) return; + p->oTags.push_back(OutTag(TAG_UNDERLINE, 0)); + p->PutTag(TAG_UNDERLINE); +} + +void Level::setFontColor(unsigned short nColor) +{ + if (m_nFontColor == nColor) return; + if (m_nFontColor) resetTag(TAG_FONT_COLOR); + if (nColor > p->colors.size()) return; + m_nFontColor = nColor; + p->oTags.push_back(OutTag(TAG_FONT_COLOR, m_nFontColor)); + p->PutTag(TAG_FONT_COLOR); +} + +void Level::setFontBgColor(unsigned short nColor) +{ + if (m_nFontBgColor == nColor) return; + if (m_nFontBgColor != 0) resetTag(TAG_BG_COLOR); + if (nColor > p->colors.size()) return; + m_nFontBgColor = nColor; + p->oTags.push_back(OutTag(TAG_BG_COLOR, m_nFontBgColor)); + p->PutTag(TAG_BG_COLOR); +} + +void Level::setFontSizeHalfPoints(unsigned short nSize) +{ + setFontSize(nSize / 2); +} + +void Level::setFontSize(unsigned short nSize) +{ + if (m_nFontSize == nSize) return; + if (m_nFontSize) resetTag(TAG_FONT_SIZE); + p->oTags.push_back(OutTag(TAG_FONT_SIZE, nSize)); + p->PutTag(TAG_FONT_SIZE); + m_nFontSize = nSize; +} + +void Level::startParagraph() +{ + // Whatever tags we have open now, close them. + // We cannot carry let character formatting tags wrap paragraphs, + // since a formatting tag can close at any time and we cannot + // close the paragraph any time we want. + resetTag(TAG_ALL); + + // Flush the current paragraph HTML to the document HTML. + p->FlushParagraph(); + + // Mark this new paragraph as an explicit one (from \par etc.). + p->bExplicitParagraph = true; + + // Restore character formatting + p->oTags.push_back(OutTag(TAG_FONT_SIZE, m_nFontSize)); + p->PutTag(TAG_FONT_SIZE); + p->oTags.push_back(OutTag(TAG_FONT_COLOR, m_nFontColor)); + p->PutTag(TAG_FONT_COLOR); + p->oTags.push_back(OutTag(TAG_FONT_FAMILY, m_nFont)); + p->PutTag(TAG_FONT_FAMILY); + if (m_nFontBgColor != 0) + { + p->oTags.push_back(OutTag(TAG_BG_COLOR, m_nFontBgColor)); + p->PutTag(TAG_BG_COLOR); + } + if (m_bBold) + { + p->oTags.push_back(OutTag(TAG_BOLD, 0)); + p->PutTag(TAG_BOLD); + } + if (m_bItalic) + { + p->PutTag(TAG_ITALIC); + p->oTags.push_back(OutTag(TAG_ITALIC, 0)); + } + if (m_bUnderline) + { + p->oTags.push_back(OutTag(TAG_UNDERLINE, 0)); + p->PutTag(TAG_UNDERLINE); + } +} + +bool Level::isParagraphOpen() const +{ + return p->bExplicitParagraph; +} + +void Level::clearParagraphFormatting() +{ + // implicitly start a paragraph + if (!isParagraphOpen()) + startParagraph(); + // Since we don't implement any of the paragraph formatting tags (e.g. alignment), + // we don't clean up anything here. Note that \pard does NOT clean character + // formatting (such as font size, font weight, italics...). + p->parStyle.clearFormatting(); +} + +void Level::setParagraphDirLTR() +{ + // implicitly start a paragraph + if (!isParagraphOpen()) + startParagraph(); + p->parStyle.dir = ParStyle::DirLTR; +} + +void Level::setParagraphDirRTL() +{ + // implicitly start a paragraph + if (!isParagraphOpen()) + startParagraph(); + p->parStyle.dir = ParStyle::DirRTL; +} + +void Level::addLineBreak() +{ + p->PrintUnquoted("<br/>"); +} + +void Level::reset() +{ + resetTag(TAG_ALL); + if (m_bColors){ + if (m_bColorInit){ + QColor c(m_nRed, m_nGreen, m_nBlue); + p->colors.push_back(c); + resetColors(); + } + return; + } +} + +void Level::setText(const char *str) +{ + if (m_bColors) + { + reset(); + } + else if (m_bFontTbl) + { + if ((m_nFont <= 0) || (m_nFont > p->fonts.size())) + return; + + FontDef& def = p->fonts[m_nFont-1]; + + char *pp = strchr(str, ';'); + unsigned size; + if (pp != NULL) + size = (pp - str); + else + size = strlen(str); + + if (m_bFontName) + { + def.nonTaggedName.append(str, size); + // We know we have the entire name + if (pp != NULL) + m_bFontName = false; + } + else if (!m_bTaggedFontNameOk) + { + def.taggedName.append(str, size); + if (pp != NULL) + m_bTaggedFontNameOk = true; + } + } + else + { + for (; *str; str++) + if ((unsigned char)(*str) >= ' ') break; + if (!*str) return; + p->FlushOutTags(); + text += str; + } +} + +void Level::flush() +{ + if (text.length() == 0) return; + // TODO: Make encoding work in Kopete + /* + const char *encoding = NULL; + if (m_nEncoding){ + for (const ENCODING *c = ICQPlugin::core->encodings; c->language; c++){ + if (!c->bMain) + continue; + if ((unsigned)c->rtf_code == m_nEncoding){ + encoding = c->codec; + break; + } + } + } + if (encoding == NULL) + encoding = p->encoding; + + QTextCodec *codec = ICQClient::_getCodec(encoding); + */ + //p->PrintQuoted(codec->toUnicode(text.c_str(), text.length())); + p->PrintQuoted(text.c_str()); + text = ""; +} + +const unsigned FONTTBL = 0; +const unsigned COLORTBL = 1; +const unsigned RED = 2; +const unsigned GREEN = 3; +const unsigned BLUE = 4; +const unsigned CF = 5; +const unsigned FS = 6; +const unsigned HIGHLIGHT = 7; +const unsigned PARD = 8; +const unsigned PAR = 9; +const unsigned I = 10; +const unsigned B = 11; +const unsigned UL = 12; +const unsigned F = 13; +const unsigned FCHARSET = 14; +const unsigned FNAME = 15; +const unsigned ULNONE = 16; +const unsigned LTRPAR = 17; +const unsigned RTLPAR = 18; +const unsigned LINE = 19; + +static char cmds[] = + "fonttbl\x00" + "colortbl\x00" + "red\x00" + "green\x00" + "blue\x00" + "cf\x00" + "fs\x00" + "highlight\x00" + "pard\x00" + "par\x00" + "i\x00" + "b\x00" + "ul\x00" + "f\x00" + "fcharset\x00" + "fname\x00" + "ulnone\x00" + "ltrpar\x00" + "rtlpar\x00" + "line\x00" + "\x00"; + +int yywrap() { return 1; } + +static char h2d(char c) +{ + if ((c >= '0') && (c <= '9')) + return c - '0'; + if ((c >= 'A') && (c <= 'F')) + return (c - 'A') + 10; + if ((c >= 'a') && (c <= 'f')) + return (c - 'a') + 10; + return 0; +} + +QString RTF2HTML::Parse(const char *rtf, const char *_encoding) +{ + encoding = _encoding; + YY_BUFFER_STATE yy_current_buffer = yy_scan_string(rtf); + rtf_ptr = rtf; + for (;;){ + int res = yylex(); + if (!res) break; + switch (res){ + case UP:{ + cur_level.flush(); + levels.push(cur_level); + break; + } + case DOWN:{ + if (!levels.empty()){ + cur_level.flush(); + cur_level.reset(); + cur_level = levels.top(); + levels.pop(); + } + break; + } + case IMG:{ + cur_level.flush(); + const char ICQIMAGE[] = "icqimage"; + const char *smiles[] = { ":-)" , ":-O" , ":-|" , ":-/" , // 0-3 + ":-(" , ":-*" , ":-/" , ":'(" , // 4-7 + ";-)" , ":-@" , ":-$" , ":-X" , // 8-B + ":-P" , "8-)" , "O:)" , ":-D" }; // C-F + const char *p = yytext + 3; + if ((strlen(p) > strlen(ICQIMAGE)) && !memcmp(p, ICQIMAGE, strlen(ICQIMAGE))){ + unsigned n = 0; + for (p += strlen(ICQIMAGE); *p; p++){ + if ((*p >= '0') && (*p <= '9')){ + n = n << 4; + n += (*p - '0'); + continue; + } + if ((*p >= 'A') && (*p <= 'F')){ + n = n << 4; + n += (*p - 'A') + 10; + continue; + } + if ((*p >= 'a') && (*p <= 'f')){ + n = n << 4; + n += (*p - 'a') + 10; + continue; + } + break; + } + if (n < 16) + PrintUnquoted(" %s ", smiles[n] ); + }else{ + kdDebug(14200) << "Unknown image " << yytext << endl; + } + break; + } + case SKIP: + break; + case SLASH: + cur_level.setText(yytext+1); + break; + case TXT: + cur_level.setText(yytext); + break; + case UNICODE_CHAR:{ + cur_level.flush(); + sParagraph += QChar((unsigned short)(atol(yytext + 2))); + break; + } + case HEX:{ + char s[2]; + s[0] = (h2d(yytext[2]) << 4) + h2d(yytext[3]); + s[1] = 0; + cur_level.setText(s); + break; + } + case CMD: + { + cur_level.flush(); + const char *cmd = yytext + 1; + unsigned n_cmd = 0; + unsigned cmd_size = 0; + int cmd_value = -1; + const char *p; + for (p = cmd; *p; p++, cmd_size++) + if (((*p >= '0') && (*p <= '9')) || (*p == ' ')) break; + if (*p && (*p != ' ')) cmd_value = atol(p); + for (p = cmds; *p; p += strlen(p) + 1, n_cmd++){ + if (strlen(p) > cmd_size) continue; + if (!memcmp(p, cmd, cmd_size)) break; + } + cmd += strlen(p); + switch (n_cmd){ + case FONTTBL: // fonttbl + cur_level.setFontTbl(); + break; + case COLORTBL: + cur_level.setColors(); + break; + case RED: + cur_level.setRed(cmd_value); + break; + case GREEN: + cur_level.setGreen(cmd_value); + break; + case BLUE: + cur_level.setBlue(cmd_value); + break; + case CF: + cur_level.setFontColor(cmd_value); + break; + case FS: + cur_level.setFontSizeHalfPoints(cmd_value); + break; + case HIGHLIGHT: + cur_level.setFontBgColor(cmd_value); + break; + case PARD: + cur_level.clearParagraphFormatting(); + break; + case PAR: + cur_level.startParagraph(); + break; + case I: + cur_level.setItalic(cmd_value != 0); + break; + case B: + cur_level.setBold(cmd_value != 0); + break; + case UL: + cur_level.setUnderline(cmd_value != 0); + break; + case ULNONE: + cur_level.setUnderline(false); + break; + case F: + // RTF fonts are 0-based; our font index is 1-based. + cur_level.setFont(cmd_value+1); + break; + case FCHARSET: + cur_level.setEncoding(cmd_value); + break; + case FNAME: + cur_level.setFontName(); + break; + case LTRPAR: + cur_level.setParagraphDirLTR(); + break; + case RTLPAR: + cur_level.setParagraphDirRTL(); + break; + case LINE: + cur_level.addLineBreak(); + } + break; + } + } + } + yy_delete_buffer(yy_current_buffer); + yy_current_buffer = NULL; + FlushParagraph(); + return s; +} + +/* +bool ICQClient::parseRTF(const char *rtf, const char *encoding, QString &res) +{ + char _RTF[] = "{\\rtf"; + if ((strlen(rtf) > strlen(_RTF)) && !memcmp(rtf, _RTF, strlen(_RTF))){ + RTF2HTML p; + res = p.Parse(rtf, encoding); + return true; + } + QTextCodec *codec = ICQClient::_getCodec(encoding); + res = codec->toUnicode(rtf, strlen(rtf)); + return false; +} +*/ diff --git a/kopete/protocols/groupwise/libgroupwise/rtf2html.h b/kopete/protocols/groupwise/libgroupwise/rtf2html.h new file mode 100644 index 00000000..a305b89d --- /dev/null +++ b/kopete/protocols/groupwise/libgroupwise/rtf2html.h @@ -0,0 +1,207 @@ +/* + rtf2html.h - A simple RTF Parser + + Copyright (c) 2002 by Vladimir Shutoff <vovan@shutoff.ru> (original code) + Copyright (c) 2004 by Thiago S. Barcelos <barcelos@ime.usp.br> (Kopete port) + Kopete (c) 2002-2003 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ************************************************************************* +*/ + +#ifndef RTF2HTML_H +#define RTF2HTML_H + +#include <qstring.h> +#include <stdio.h> + +#include <qtextcodec.h> +#include <qcolor.h> +#include <qregexp.h> +#include <kdebug.h> + +#include <vector> +#include <stack> +#include <string> +#include <stdarg.h> + +using namespace std; + +struct FontDef +{ + int charset; + string taggedName; + string nonTaggedName; +}; + +class RTF2HTML; + +enum TagEnum +{ + TAG_ALL = 0, + TAG_FONT_SIZE, + TAG_FONT_COLOR, + TAG_FONT_FAMILY, + TAG_BG_COLOR, + TAG_BOLD, + TAG_ITALIC, + TAG_UNDERLINE +}; + +class ParStyle +{ +public: + ParStyle() { dir = DirLTR; } + void clearFormatting(); + +public: + enum {DirLTR, DirRTL} dir; +}; + +class Level +{ +public: + Level(RTF2HTML *_p); + Level(const Level&); + void setText(const char* str); + void setFontTbl() { m_bFontTbl = true; } + void setColors() { m_bColors = true; resetColors(); } + void setRed(unsigned char val) { setColor(val, &m_nRed); } + void setGreen(unsigned char val) { setColor(val, &m_nGreen); } + void setBlue(unsigned char val) { setColor(val, &m_nBlue); } + void setFont(unsigned nFont); + void setEncoding(unsigned nFont); + void setFontName(); + void setFontColor(unsigned short color); + void setFontBgColor(unsigned short color); + void setFontSizeHalfPoints(unsigned short sizeInHalfPoints); + void setFontSize(unsigned short sizeInPoints); + void setBold(bool); + void setItalic(bool); + void setUnderline(bool); + void startParagraph(); + bool isParagraphOpen() const; + void clearParagraphFormatting(); + void setParagraphDirLTR(); + void setParagraphDirRTL(); + void addLineBreak(); + void flush(); + void reset(); + void resetTag(TagEnum tag); +protected: + string text; + void Init(); + RTF2HTML *p; + void resetColors() { m_nRed = m_nGreen = m_nBlue = 0; m_bColorInit = false; } + void setColor(unsigned char val, unsigned char *p) + { *p = val; m_bColorInit=true; } + + // Marks the position in m_tags where this level begun. + unsigned m_nTagsStartPos; + + // True when parsing the fonts table + bool m_bFontTbl; + // True when parsing the colors table. + bool m_bColors; + // True when inside a 'fname' block. + bool m_bFontName; + // False until we get the tagged font name. + bool m_bTaggedFontNameOk; + + unsigned char m_nRed; + unsigned char m_nGreen; + unsigned char m_nBlue; + bool m_bColorInit; + unsigned m_nFont; // 1-based + unsigned m_nEncoding; + unsigned m_nFontColor; // 1-based + unsigned m_nFontSize; + unsigned m_nFontBgColor; // 1-based + bool m_bBold; + bool m_bItalic; + bool m_bUnderline; +}; + +class OutTag +{ +public: + OutTag(TagEnum _tag, unsigned _param) : tag(_tag), param(_param) {} + TagEnum tag; + unsigned param; +}; + +enum quoteMode +{ + quoteHTML, + quoteXML, + quoteNOBR +}; + +class RTF2HTML +{ + friend class Level; + +public: + RTF2HTML(); + QString Parse(const char *rtf, const char *encoding); + + // Paragraph-specific functions: + + QString quoteString(const QString &_str, quoteMode mode = quoteHTML); + // Appends a string with formatting into the paragraph buffer. + void PrintUnquoted(const char *str, ...); + // Quotes and appends a string to the paragraph buffer. + void PrintQuoted(const QString &str); + // Writes down the tags from oTags into the paragraph buffer. + void FlushOutTags(); + // Retrieves the top not-yet-written tag. + OutTag* getTopOutTag(TagEnum tagType); + // Writes down the paragraph buffer and resets the paragraph state. + void FlushParagraph(); + + // Document-wide functions: + + void PutTag(TagEnum n) + { + tags.push(n); + } + +protected: + +// Paragraph members + + // True if the paragraph was opened explicitly. + bool bExplicitParagraph; + // The paragraph's HTML buffer. + QString sParagraph; + // Defines the paragraph's formatting. + ParStyle parStyle; + // Tags which weren't yet printed out. + vector<OutTag> oTags; + +// Document members + + // The document HTML buffer. + QString s; + // Fonts table. + vector<FontDef> fonts; + // Colors table. + vector<QColor> colors; + // Stack of tags (across all levels, not just current level) + stack<TagEnum> tags; + +// RTF parser internals + + const char *rtf_ptr; + const char *encoding; + Level cur_level; + stack<Level> levels; +}; + +#endif diff --git a/kopete/protocols/groupwise/libgroupwise/safedelete.cpp b/kopete/protocols/groupwise/libgroupwise/safedelete.cpp new file mode 100644 index 00000000..703e8ed3 --- /dev/null +++ b/kopete/protocols/groupwise/libgroupwise/safedelete.cpp @@ -0,0 +1,139 @@ +/* + safedelete.cpp - Kopete Groupwise Protocol + + Copyright (c) 2004 SUSE Linux AG http://www.suse.com + + Based on Iris, Copyright (C) 2003 Justin Karneges + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#include "safedelete.h" + +#include <qtimer.h> + +//---------------------------------------------------------------------------- +// SafeDelete +//---------------------------------------------------------------------------- +SafeDelete::SafeDelete() +{ + lock = 0; +} + +SafeDelete::~SafeDelete() +{ + if(lock) + lock->dying(); +} + +void SafeDelete::deleteLater(QObject *o) +{ + if(!lock) + deleteSingle(o); + else + list.append(o); +} + +void SafeDelete::unlock() +{ + lock = 0; + deleteAll(); +} + +void SafeDelete::deleteAll() +{ + if(list.isEmpty()) + return; + + QObjectListIt it(list); + for(QObject *o; (o = it.current()); ++it) + deleteSingle(o); + list.clear(); +} + +void SafeDelete::deleteSingle(QObject *o) +{ +#if QT_VERSION < 0x030000 + // roll our own QObject::deleteLater() + SafeDeleteLater *sdl = SafeDeleteLater::ensureExists(); + sdl->deleteItLater(o); +#else + o->deleteLater(); +#endif +} + +//---------------------------------------------------------------------------- +// SafeDeleteLock +//---------------------------------------------------------------------------- +SafeDeleteLock::SafeDeleteLock(SafeDelete *sd) +{ + own = false; + if(!sd->lock) { + _sd = sd; + _sd->lock = this; + } + else + _sd = 0; +} + +SafeDeleteLock::~SafeDeleteLock() +{ + if(_sd) { + _sd->unlock(); + if(own) + delete _sd; + } +} + +void SafeDeleteLock::dying() +{ + _sd = new SafeDelete(*_sd); + own = true; +} + +//---------------------------------------------------------------------------- +// SafeDeleteLater +//---------------------------------------------------------------------------- +SafeDeleteLater *SafeDeleteLater::self = 0; + +SafeDeleteLater *SafeDeleteLater::ensureExists() +{ + if(!self) + new SafeDeleteLater(); + return self; +} + +SafeDeleteLater::SafeDeleteLater() +{ + list.setAutoDelete(true); + self = this; + QTimer::singleShot(0, this, SLOT(explode())); +} + +SafeDeleteLater::~SafeDeleteLater() +{ + list.clear(); + self = 0; +} + +void SafeDeleteLater::deleteItLater(QObject *o) +{ + list.append(o); +} + +void SafeDeleteLater::explode() +{ + delete this; +} + +#include "safedelete.moc" + diff --git a/kopete/protocols/groupwise/libgroupwise/safedelete.h b/kopete/protocols/groupwise/libgroupwise/safedelete.h new file mode 100644 index 00000000..e8215c06 --- /dev/null +++ b/kopete/protocols/groupwise/libgroupwise/safedelete.h @@ -0,0 +1,79 @@ +/* + gwclientstream.h - Kopete Groupwise Protocol + + Copyright (c) 2004 SUSE Linux AG http://www.suse.com + + Based on Iris, Copyright (C) 2003 Justin Karneges + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#ifndef SAFEDELETE_H +#define SAFEDELETE_H + +#include<qobject.h> +#include<qobjectlist.h> + +class SafeDelete; +class SafeDeleteLock +{ +public: + SafeDeleteLock(SafeDelete *sd); + ~SafeDeleteLock(); + +private: + SafeDelete *_sd; + bool own; + friend class SafeDelete; + void dying(); +}; + +class SafeDelete +{ +public: + SafeDelete(); + ~SafeDelete(); + + void deleteLater(QObject *o); + + // same as QObject::deleteLater() + static void deleteSingle(QObject *o); + +private: + QObjectList list; + void deleteAll(); + + friend class SafeDeleteLock; + SafeDeleteLock *lock; + void unlock(); +}; + +class SafeDeleteLater : public QObject +{ + Q_OBJECT +public: + static SafeDeleteLater *ensureExists(); + void deleteItLater(QObject *o); + +private slots: + void explode(); + +private: + SafeDeleteLater(); + ~SafeDeleteLater(); + + QObjectList list; + friend class SafeDelete; + static SafeDeleteLater *self; +}; + +#endif diff --git a/kopete/protocols/groupwise/libgroupwise/securestream.cpp b/kopete/protocols/groupwise/libgroupwise/securestream.cpp new file mode 100644 index 00000000..583be03e --- /dev/null +++ b/kopete/protocols/groupwise/libgroupwise/securestream.cpp @@ -0,0 +1,542 @@ +/* + securestream.h - Kopete Groupwise Protocol + Combines a ByteStream with TLS and SASL + + Copyright (c) 2004 SUSE Linux AG http://www.suse.com + + Based on Iris, Copyright (C) 2003 Justin Karneges + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +/* + Note: SecureStream depends on the underlying security layers to signal + plain-to-encrypted results immediately (as opposed to waiting for the + event loop) so that the user cannot add/remove security layers during + this conversion moment. QCA::TLS and QCA::SASL behave as expected, + but future layers might not. +*/ + +#include<qguardedptr.h> +#include<qvaluelist.h> +#include<qtimer.h> + +#include"securestream.h" + +//---------------------------------------------------------------------------- +// LayerTracker +//---------------------------------------------------------------------------- +LayerTracker::LayerTracker() +{ + p = 0; +} + +void LayerTracker::reset() +{USE_TLSHANDLER + p = 0; + list.clear(); +} + +void LayerTracker::addPlain(int plain) +{ + p += plain; +} + +void LayerTracker::specifyEncoded(int encoded, int plain) +{ + // can't specify more bytes than we have + if(plain > p) + plain = p; + p -= plain; + Item i; + i.plain = plain; + i.encoded = encoded; + list += i; +} + +int LayerTracker::finished(int encoded) +{ + int plain = 0; + for(QValueList<Item>::Iterator it = list.begin(); it != list.end();) { + Item &i = *it; + + // not enough? + if(encoded < i.encoded) { + i.encoded -= encoded; + break; + } + + encoded -= i.encoded; + plain += i.plain; + it = list.remove(it); + } + return plain; +} + +//---------------------------------------------------------------------------- +// SecureStream +//---------------------------------------------------------------------------- + +SecureLayer::SecureLayer(QCA::TLS *t) +{ + type = TLS; + p.tls = t; + init(); + connect(p.tls, SIGNAL(handshaken()), SLOT(tls_handshaken())); + connect(p.tls, SIGNAL(readyRead()), SLOT(tls_readyRead())); + connect(p.tls, SIGNAL(readyReadOutgoing(int)), SLOT(tls_readyReadOutgoing(int))); + connect(p.tls, SIGNAL(closed()), SLOT(tls_closed())); + connect(p.tls, SIGNAL(error(int)), SLOT(tls_error(int))); +} + +SecureLayer::SecureLayer(QCA::SASL *s) +{ + type = SASL; + p.sasl = s; + init(); + connect(p.sasl, SIGNAL(readyRead()), SLOT(sasl_readyRead())); + connect(p.sasl, SIGNAL(readyReadOutgoing(int)), SLOT(sasl_readyReadOutgoing(int))); + connect(p.sasl, SIGNAL(error(int)), SLOT(sasl_error(int))); +} + +#ifdef USE_TLSHANDLER +SecureLayer::SecureLayer(TLSHandler *t) +{ + type = TLSH; + p.tlsHandler = t; + init(); + connect(p.tlsHandler, SIGNAL(success()), SLOT(tlsHandler_success())); + connect(p.tlsHandler, SIGNAL(fail()), SLOT(tlsHandler_fail())); + connect(p.tlsHandler, SIGNAL(closed()), SLOT(tlsHandler_closed())); + connect(p.tlsHandler, SIGNAL(readyRead(const QByteArray &)), SLOT(tlsHandler_readyRead(const QByteArray &))); + connect(p.tlsHandler, SIGNAL(readyReadOutgoing(const QByteArray &, int)), SLOT(tlsHandler_readyReadOutgoing(const QByteArray &, int))); +} +#endif + +void SecureLayer::init() +{ + tls_done = false; + prebytes = 0; +} + +void SecureLayer::write(const QByteArray &a) +{ + layer.addPlain(a.size()); + switch(type) { + case TLS: { p.tls->write(a); break; } + case SASL: { p.sasl->write(a); break; } +#ifdef USE_TLSHANDLER + case TLSH: { p.tlsHandler->write(a); break; } +#endif + } +} + +void SecureLayer::writeIncoming(const QByteArray &a) +{ + switch(type) { + case TLS: { p.tls->writeIncoming(a); break; } + case SASL: { p.sasl->writeIncoming(a); break; } +#ifdef USE_TLSHANDLER + case TLSH: { p.tlsHandler->writeIncoming(a); break; } +#endif + } +} + +int SecureLayer::finished(int plain) +{ + int written = 0; + + // deal with prebytes (bytes sent prior to this security layer) + if(prebytes > 0) { + if(prebytes >= plain) { + written += plain; + prebytes -= plain; + plain = 0; + } + else { + written += prebytes; + plain -= prebytes; + prebytes = 0; + } + } + + // put remainder into the layer tracker + if(type == SASL || tls_done) + written += layer.finished(plain); + + return written; +} + +void SecureLayer::tls_handshaken() +{ + tls_done = true; + tlsHandshaken(); +} + +void SecureLayer::tls_readyRead() +{ + QByteArray a = p.tls->read(); + readyRead(a); +} + +void SecureLayer::tls_readyReadOutgoing(int plainBytes) +{ + QByteArray a = p.tls->readOutgoing(); + if(tls_done) + layer.specifyEncoded(a.size(), plainBytes); + needWrite(a); +} + +void SecureLayer::tls_closed() +{ + QByteArray a = p.tls->readUnprocessed(); + tlsClosed(a); +} + +void SecureLayer::tls_error(int x) +{ + error(x); +} + +void SecureLayer::sasl_readyRead() +{ + QByteArray a = p.sasl->read(); + readyRead(a); +} + +void SecureLayer::sasl_readyReadOutgoing(int plainBytes) +{ + QByteArray a = p.sasl->readOutgoing(); + layer.specifyEncoded(a.size(), plainBytes); + needWrite(a); +} + +void SecureLayer::sasl_error(int x) +{ + error(x); +} + +#ifdef USE_TLSHANDLER +void SecureLayer::tlsHandler_success() +{ + tls_done = true; + tlsHandshaken(); +} + +void SecureLayer::tlsHandler_fail() +{ + error(0); +} + +void SecureLayer::tlsHandler_closed() +{ + tlsClosed(QByteArray()); +} + +void SecureLayer::tlsHandler_readyRead(const QByteArray &a) +{ + readyRead(a); +} + +void SecureLayer::tlsHandler_readyReadOutgoing(const QByteArray &a, int plainBytes) +{ + if(tls_done) + layer.specifyEncoded(a.size(), plainBytes); + needWrite(a); +} +#endif + +class SecureStream::Private +{ +public: + ByteStream *bs; + QPtrList<SecureLayer> layers; + int pending; + int errorCode; + bool active; + bool topInProgress; + + bool haveTLS() const + { + QPtrListIterator<SecureLayer> it(layers); + for(SecureLayer *s; (s = it.current()); ++it) { + if(s->type == SecureLayer::TLS +#ifdef USE_TLSHANDLER + || s->type == SecureLayer::TLSH +#endif + ) { + return true; + } + } + return false; + } + + bool haveSASL() const + { + QPtrListIterator<SecureLayer> it(layers); + for(SecureLayer *s; (s = it.current()); ++it) { + if(s->type == SecureLayer::SASL) + return true; + } + return false; + } +}; + +SecureStream::SecureStream(ByteStream *s) +:ByteStream(0) +{ + d = new Private; + + d->bs = s; + connect(d->bs, SIGNAL(readyRead()), SLOT(bs_readyRead())); + connect(d->bs, SIGNAL(bytesWritten(int)), SLOT(bs_bytesWritten(int))); + + d->layers.setAutoDelete(true); + d->pending = 0; + d->active = true; + d->topInProgress = false; +} + +SecureStream::~SecureStream() +{ + delete d; +} + +void SecureStream::linkLayer(QObject *s) +{ + connect(s, SIGNAL(tlsHandshaken()), SLOT(layer_tlsHandshaken())); + connect(s, SIGNAL(tlsClosed(const QByteArray &)), SLOT(layer_tlsClosed(const QByteArray &))); + connect(s, SIGNAL(readyRead(const QByteArray &)), SLOT(layer_readyRead(const QByteArray &))); + connect(s, SIGNAL(needWrite(const QByteArray &)), SLOT(layer_needWrite(const QByteArray &))); + connect(s, SIGNAL(error(int)), SLOT(layer_error(int))); +} + +int SecureStream::calcPrebytes() const +{ + int x = 0; + QPtrListIterator<SecureLayer> it(d->layers); + for(SecureLayer *s; (s = it.current()); ++it) + x += s->prebytes; + return (d->pending - x); +} + +void SecureStream::startTLSClient(QCA::TLS *t, const QByteArray &spare) +{ + if(!d->active || d->topInProgress || d->haveTLS()) + return; + + SecureLayer *s = new SecureLayer(t); + s->prebytes = calcPrebytes(); + linkLayer(s); + d->layers.append(s); + d->topInProgress = true; + + insertData(spare); +} + +void SecureStream::startTLSServer(QCA::TLS *t, const QByteArray &spare) +{ + if(!d->active || d->topInProgress || d->haveTLS()) + return; + + SecureLayer *s = new SecureLayer(t); + s->prebytes = calcPrebytes(); + linkLayer(s); + d->layers.append(s); + d->topInProgress = true; + + insertData(spare); +} + +void SecureStream::setLayerSASL(QCA::SASL *sasl, const QByteArray &spare) +{ + if(!d->active || d->topInProgress || d->haveSASL()) + return; + + SecureLayer *s = new SecureLayer(sasl); + s->prebytes = calcPrebytes(); + linkLayer(s); + d->layers.append(s); + + insertData(spare); +} + +#ifdef USE_TLSHANDLER +void SecureStream::startTLSClient(TLSHandler *t, const QString &server, const QByteArray &spare) +{ + if(!d->active || d->topInProgress || d->haveTLS()) + return; + + SecureLayer *s = new SecureLayer(t); + s->prebytes = calcPrebytes(); + linkLayer(s); + d->layers.append(s); + d->topInProgress = true; + + // unlike QCA::TLS, TLSHandler has no return value + s->p.tlsHandler->startClient(server); + + insertData(spare); +} +#endif + +void SecureStream::closeTLS() +{ + SecureLayer *s = d->layers.getLast(); + if(s) { + if(s->type == SecureLayer::TLS) + s->p.tls->close(); + } +} + +int SecureStream::errorCode() const +{ + return d->errorCode; +} + +bool SecureStream::isOpen() const +{ + return d->active; +} + +void SecureStream::write(const QByteArray &a) +{ + if(!isOpen()) + return; + + d->pending += a.size(); + + // send to the last layer + SecureLayer *s = d->layers.getLast(); + if(s) + s->write(a); + else + writeRawData(a); +} + +int SecureStream::bytesToWrite() const +{ + return d->pending; +} + +void SecureStream::bs_readyRead() +{ + QByteArray a = d->bs->read(); + + // send to the first layer + SecureLayer *s = d->layers.getFirst(); + if(s) + s->writeIncoming(a); + else + incomingData(a); +} + +void SecureStream::bs_bytesWritten(int bytes) +{ + QPtrListIterator<SecureLayer> it(d->layers); + for(SecureLayer *s; (s = it.current()); ++it) + bytes = s->finished(bytes); + + if(bytes > 0) { + d->pending -= bytes; + bytesWritten(bytes); + } +} + +void SecureStream::layer_tlsHandshaken() +{ + d->topInProgress = false; + tlsHandshaken(); +} + +void SecureStream::layer_tlsClosed(const QByteArray &) +{ + d->active = false; + d->layers.clear(); + tlsClosed(); +} + +void SecureStream::layer_readyRead(const QByteArray &a) +{ + SecureLayer *s = (SecureLayer *)sender(); + QPtrListIterator<SecureLayer> it(d->layers); + while(it.current() != s) + ++it; + + // pass upwards + ++it; + s = it.current(); + if(s) + s->writeIncoming(a); + else + incomingData(a); +} + +void SecureStream::layer_needWrite(const QByteArray &a) +{ + SecureLayer *s = (SecureLayer *)sender(); + QPtrListIterator<SecureLayer> it(d->layers); + while(it.current() != s) + ++it; + + // pass downwards + --it; + s = it.current(); + if(s) + s->write(a); + else + writeRawData(a); +} + +void SecureStream::layer_error(int x) +{ + SecureLayer *s = (SecureLayer *)sender(); + int type = s->type; + d->errorCode = x; + d->active = false; + d->layers.clear(); + if(type == SecureLayer::TLS) + error(ErrTLS); + else if(type == SecureLayer::SASL) + error(ErrSASL); +#ifdef USE_TLSHANDLER + else if(type == SecureLayer::TLSH) + error(ErrTLS); +#endif +} + +void SecureStream::insertData(const QByteArray &a) +{ + if(!a.isEmpty()) { + SecureLayer *s = d->layers.getLast(); + if(s) + s->writeIncoming(a); + else + incomingData(a); + } +} + +void SecureStream::writeRawData(const QByteArray &a) +{ + d->bs->write(a); +} + +void SecureStream::incomingData(const QByteArray &a) +{ + appendRead(a); + //qDebug( "SecureStream::incomingData() got %i bytes ", a.size() ); + + if(bytesAvailable()) + readyRead(); +} + +#include "securestream.moc" diff --git a/kopete/protocols/groupwise/libgroupwise/securestream.h b/kopete/protocols/groupwise/libgroupwise/securestream.h new file mode 100644 index 00000000..36999b14 --- /dev/null +++ b/kopete/protocols/groupwise/libgroupwise/securestream.h @@ -0,0 +1,156 @@ +/* + securestream.h - Kopete Groupwise Protocol + Combines a ByteStream with TLS and SASL + + Copyright (c) 2004 SUSE Linux AG http://www.suse.com + + Based on Iris, Copyright (C) 2003 Justin Karneges + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#ifndef SECURESTREAM_H +#define SECURESTREAM_H + +#include<qca.h> +#include "tlshandler.h" +#include"bytestream.h" + +#define USE_TLSHANDLER + +#ifdef USE_TLSHANDLER + class TLSHandler; +#endif + +class SecureStream : public ByteStream +{ + Q_OBJECT +public: + enum Error { ErrTLS = ErrCustom, ErrSASL }; + SecureStream(ByteStream *s); + ~SecureStream(); + + void startTLSClient(QCA::TLS *t, const QByteArray &spare=QByteArray()); + void startTLSServer(QCA::TLS *t, const QByteArray &spare=QByteArray()); + void setLayerSASL(QCA::SASL *s, const QByteArray &spare=QByteArray()); +#ifdef USE_TLSHANDLER + void startTLSClient(TLSHandler *t, const QString &server, const QByteArray &spare=QByteArray()); +#endif + + void closeTLS(); + int errorCode() const; + + // reimplemented + bool isOpen() const; + void write(const QByteArray &); + int bytesToWrite() const; + +signals: + void tlsHandshaken(); + void tlsClosed(); + +private slots: + void bs_readyRead(); + void bs_bytesWritten(int); + + void layer_tlsHandshaken(); + void layer_tlsClosed(const QByteArray &); + void layer_readyRead(const QByteArray &); + void layer_needWrite(const QByteArray &); + void layer_error(int); + +private: + void linkLayer(QObject *); + int calcPrebytes() const; + void insertData(const QByteArray &a); + void writeRawData(const QByteArray &a); + void incomingData(const QByteArray &a); + + class Private; + Private *d; +}; + +class LayerTracker +{ +public: + struct Item + { + int plain; + int encoded; + }; +USE_TLSHANDLER + LayerTracker(); + + void reset(); + void addPlain(int plain); + void specifyEncoded(int encoded, int plain); + int finished(int encoded); + + int p; + QValueList<Item> list; +}; + + +class SecureLayer : public QObject +{ + Q_OBJECT +public: + SecureLayer(QCA::TLS *t); + SecureLayer(QCA::SASL *s); +#ifdef USE_TLSHANDLER + SecureLayer(TLSHandler *t); +#endif + void init(); + void write(const QByteArray &a); + void writeIncoming(const QByteArray &a); + int finished(int plain); + + enum { TLS, SASL, TLSH }; + int type; + union { + QCA::TLS *tls; + QCA::SASL *sasl; +#ifdef USE_TLSHANDLER + TLSHandler *tlsHandler; +#endif + } p; + LayerTracker layer; + bool tls_done; + int prebytes; + +signals: + void tlsHandshaken(); + void tlsClosed(const QByteArray &); + void readyRead(const QByteArray &); + void needWrite(const QByteArray &); + void error(int); + +private slots: + void tls_handshaken(); + void tls_readyRead(); + void tls_readyReadOutgoing(int plainBytes); + void tls_closed(); + void tls_error(int x); + void sasl_readyRead(); + void sasl_readyReadOutgoing(int plainBytes); + void sasl_error(int x); +#ifdef USE_TLSHANDLER + void tlsHandler_success(); + void tlsHandler_fail(); + void tlsHandler_closed(); + void tlsHandler_readyRead(const QByteArray &a); + void tlsHandler_readyReadOutgoing(const QByteArray &a, int plainBytes); +#endif + +}; + +#endif diff --git a/kopete/protocols/groupwise/libgroupwise/stream.cpp b/kopete/protocols/groupwise/libgroupwise/stream.cpp new file mode 100644 index 00000000..5817f4c3 --- /dev/null +++ b/kopete/protocols/groupwise/libgroupwise/stream.cpp @@ -0,0 +1,31 @@ +/* + stream.cpp - Kopete Groupwise Protocol + + Copyright (c) 2004 SUSE Linux AG http://www.suse.com + + Based on Iris, Copyright (C) 2003 Justin Karneges + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#include "stream.h" + +Stream::Stream(QObject *parent) +:QObject(parent) +{ +} + +Stream::~Stream() +{ +} + +#include "stream.moc" diff --git a/kopete/protocols/groupwise/libgroupwise/stream.h b/kopete/protocols/groupwise/libgroupwise/stream.h new file mode 100644 index 00000000..37a63652 --- /dev/null +++ b/kopete/protocols/groupwise/libgroupwise/stream.h @@ -0,0 +1,92 @@ +/* + stream.h - Kopete Groupwise Protocol + + Copyright (c) 2004 SUSE Linux AG http://www.suse.com + + Based on Iris, Copyright (C) 2003 Justin Karneges + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#include <qdom.h> +#include "qobject.h" + +#include "gwerror.h" +#include "gwfield.h" +#include "request.h" + +#ifndef GW_STREAM_H +#define GW_STREAM_H + + +class Stream : public QObject +{ + Q_OBJECT +public: + enum Error { ErrParse, ErrProtocol, ErrStream, ErrCustom = 10 }; + enum StreamCond { + GenericStreamError, + Conflict, + ConnectionTimeout, + InternalServerError, + InvalidFrom, +/*# InvalidXml, // not required*/ + PolicyViolation, + ResourceConstraint, + SystemShutdown + }; + + Stream(QObject *parent=0); + virtual ~Stream(); + + virtual void close()=0; + virtual int errorCondition() const=0; + virtual QString errorText() const=0; + //virtual QDomElement errorAppSpec() const=0; + + /** + * Are there any messages waiting to be read + */ + virtual bool transfersAvailable() const = 0; // adapt to messages + /** + * Read a message received from the server + */ + virtual Transfer * read() = 0; + + /** + * Send a message to the server + */ + virtual void write( Request *request) = 0; // ", ends up on a send queue, by a very roundabout way, see analysis at bottom of + +// # virtual bool stanzaAvailable() const=0; +// # virtual Stanza read()=0; +// # virtual void write(const Stanza &s)=0; + +// # virtual QDomDocument & doc() const=0; +// # virtual QString baseNS() const=0; +// # virtual bool old() const=0; + +// # Stanza createStanza(Stanza::Kind k, const Jid &to="", const QString &type="", const QString &id=""); +// # Stanza createStanza(const QDomElement &e); + +// static QString xmlToString(const static XmlProtocol *foo = 0; +//QDomElement &e, bool clip=false); + +signals: + void connectionClosed(); + void delayedCloseFinished(); + void readyRead(); +// void stanzaWritten(); + void error(int); +}; + +#endif diff --git a/kopete/protocols/groupwise/libgroupwise/task.cpp b/kopete/protocols/groupwise/libgroupwise/task.cpp new file mode 100644 index 00000000..786bf36b --- /dev/null +++ b/kopete/protocols/groupwise/libgroupwise/task.cpp @@ -0,0 +1,268 @@ +/* + task.cpp - Kopete Groupwise Protocol + + Copyright (c) 2004 SUSE Linux AG http://www.suse.com + + Based on Iris, Copyright (C) 2003 Justin Karneges + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#include <qtimer.h> + +#include "client.h" +#include "gwfield.h" +#include "request.h" +#include "safedelete.h" + +#include "task.h" + +class Task::TaskPrivate +{ +public: + TaskPrivate() {} + + QString id; + bool success; + int statusCode; + QString statusString; + Client *client; + bool insignificant, deleteme, autoDelete; + bool done; + Transfer * transfer; +}; + +Task::Task(Task *parent) +:QObject(parent) +{ + init(); + d->transfer = 0; + d->client = parent->client(); + d->id = client()->genUniqueId(); + connect(d->client, SIGNAL(disconnected()), SLOT(clientDisconnected())); +} + +Task::Task(Client *parent, bool) +:QObject(0) +{ + init(); + + d->client = parent; + connect(d->client, SIGNAL(disconnected()), SLOT(clientDisconnected())); +} + +Task::~Task() +{ + delete d; +} + +void Task::init() +{ + d = new TaskPrivate; + d->success = false; + d->insignificant = false; + d->deleteme = false; + d->autoDelete = false; + d->done = false; + d->transfer = 0; + d->statusCode = 0; +} + +Task *Task::parent() const +{ + return (Task *)QObject::parent(); +} + +Client *Task::client() const +{ + return d->client; +} + +Transfer * Task::transfer() const +{ + return d->transfer; +} + +void Task::setTransfer( Transfer * transfer ) +{ + d->transfer = transfer; +} + +QString Task::id() const +{ + return d->id; +} + +bool Task::success() const +{ + return d->success; +} + +int Task::statusCode() const +{ + return d->statusCode; +} + +const QString & Task::statusString() const +{ + return d->statusString; +} + +void Task::go(bool autoDelete) +{ + d->autoDelete = autoDelete; + + onGo(); +} + +bool Task::take( Transfer * transfer) +{ + const QObjectList *p = children(); + if(!p) + return false; + + // pass along the transfer to our children + QObjectListIt it(*p); + Task *t; + for(; it.current(); ++it) { + QObject *obj = it.current(); + if(!obj->inherits("Task")) + continue; + + t = static_cast<Task*>(obj); + + if(t->take( transfer )) + { + client()->debug( QString( "Transfer ACCEPTED by: %1" ).arg( t->className() ) ); + return true; + } + } + + return false; +} + +void Task::safeDelete() +{ + if(d->deleteme) + return; + + d->deleteme = true; + if(!d->insignificant) + SafeDelete::deleteSingle(this); +} + +void Task::onGo() +{ + client()->debug( "ERROR: calling default NULL onGo() for this task, you should reimplement this!"); +} + +void Task::onDisconnect() +{ + if(!d->done) { + d->success = false; + d->statusCode = ErrDisc; + d->statusString = tr("Disconnected"); + + // delay this so that tasks that react don't block the shutdown + QTimer::singleShot(0, this, SLOT(done())); + } +} + +void Task::send( Request * request ) +{ + client()->send( request ); +} + +void Task::setSuccess(int code, const QString &str) +{ + if(!d->done) { + d->success = true; + d->statusCode = code; + d->statusString = str; + done(); + } +} + +void Task::setError(int code, const QString &str) +{ + if(!d->done) { + d->success = false; + d->statusCode = code; + if ( str.isEmpty() ) + d->statusString = GroupWise::errorCodeToString( code ); + else + d->statusString = str; + done(); + } +} + +void Task::done() +{ + debug("Task::done()"); + if(d->done || d->insignificant) + return; + d->done = true; + + if(d->deleteme || d->autoDelete) + d->deleteme = true; + + d->insignificant = true; + debug("emitting finished"); + finished(); + d->insignificant = false; + + if(d->deleteme) + SafeDelete::deleteSingle(this); +} + +void Task::clientDisconnected() +{ + onDisconnect(); +} + +// void Task::debug(const char *fmt, ...) +// { +// char *buf; +// QString str; +// int size = 1024; +// int r; +// +// do { +// buf = new char[size]; +// va_list ap; +// va_start(ap, fmt); +// r = vsnprintf(buf, size, fmt, ap); +// va_end(ap); +// +// if(r != -1) +// str = QString(buf); +// +// delete [] buf; +// +// size *= 2; +// } while(r == -1); +// +// debug(str); +// } + +void Task::debug(const QString &str) +{ + client()->debug(QString("%1: ").arg(className()) + str); +} + +bool Task::forMe( const Transfer * transfer ) const +{ + Q_UNUSED( transfer ); + return false; +} + +#include "task.moc" diff --git a/kopete/protocols/groupwise/libgroupwise/task.h b/kopete/protocols/groupwise/libgroupwise/task.h new file mode 100644 index 00000000..0a34cafa --- /dev/null +++ b/kopete/protocols/groupwise/libgroupwise/task.h @@ -0,0 +1,97 @@ +/* + task.h - Kopete Groupwise Protocol + + Copyright (c) 2004 SUSE Linux AG http://www.suse.com + + Based on Iris, Copyright (C) 2003 Justin Karneges + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#ifndef GW_TASK_H +#define GW_TASK_H + +#include <qobject.h> + +#include "gwerror.h" +#include "gwfield.h" +#include "transfer.h" + +class Client; +class Request; + +class Task : public QObject +{ + Q_OBJECT +public: + enum { ErrDisc }; + Task(Task *parent); + Task( Client *, bool isRoot ); + virtual ~Task(); + + Task *parent() const; + Client *client() const; + Transfer *transfer() const; + + QString id() const; + + bool success() const; + int statusCode() const; + const QString & statusString() const; + + void go( bool autoDelete=false ); + /** + * Allows a task to examine an incoming Transfer and decide whether to 'take' it + * for further processing. + */ + virtual bool take( Transfer* transfer ); + void safeDelete(); + +signals: + void finished(); + +protected: + virtual void onGo(); + virtual void onDisconnect(); + void send( Request * request ); + void setSuccess( int code=0, const QString &str="" ); + /** + * If an empty string is passed, this sets the error string based on the error code using GroupWise::errorCodeToString + */ + void setError( int code=0, const QString &str="" ); +// void debug( const char *, ... ); + void debug( const QString & ); + /** + * Used in take() to check if the offered transfer is for this Task + * @return true if this Task should take the Transfer. Default impl always returns false. + */ + virtual bool forMe( const Transfer * transfer ) const; + /** + * Creates a transfer with the given command and field list + */ + void createTransfer( const QString & command, const Field::FieldList fields ); + /** + * Direct setter for Tasks which don't have any fields + */ + void setTransfer( Transfer * transfer ); +private slots: + void clientDisconnected(); + void done(); + +private: + void init(); + + class TaskPrivate; + TaskPrivate *d; +}; + +#endif diff --git a/kopete/protocols/groupwise/libgroupwise/tasks/Makefile.am b/kopete/protocols/groupwise/libgroupwise/tasks/Makefile.am new file mode 100644 index 00000000..cf966ca2 --- /dev/null +++ b/kopete/protocols/groupwise/libgroupwise/tasks/Makefile.am @@ -0,0 +1,30 @@ +INCLUDES = -I$(top_srcdir)/protocols/groupwise/libgroupwise/qca/src \ + -I$(srcdir)/../../libgroupwise/ -I$(top_srcdir)/kopete/protocols/groupwise/libgroupwise/qca/src \ + $(all_includes) +METASOURCES = AUTO +noinst_LTLIBRARIES = libgroupwise_tasks.la + +libgroupwise_tasks_la_SOURCES = requesttask.cpp eventtask.cpp logintask.cpp \ + setstatustask.cpp statustask.cpp conferencetask.cpp createconferencetask.cpp \ + sendmessagetask.cpp getdetailstask.cpp getstatustask.cpp typingtask.cpp connectiontask.cpp \ + sendinvitetask.cpp joinconferencetask.cpp leaveconferencetask.cpp rejectinvitetask.cpp \ + keepalivetask.cpp createcontacttask.cpp modifycontactlisttask.cpp createfoldertask.cpp \ + movecontacttask.cpp updateitemtask.cpp createcontactinstancetask.cpp deleteitemtask.cpp \ + updatefoldertask.cpp updatecontacttask.cpp pollsearchresultstask.cpp privacyitemtask.cpp \ + needfoldertask.cpp searchchattask.cpp searchusertask.cpp searchusertask.h \ + getchatsearchresultstask.cpp chatcountstask.cpp chatpropertiestask.cpp joinchattask.cpp +noinst_HEADERS = requesttask.h eventtask.h logintask.h setstatustask.h \ + statustask.h conferencetask.h createconferencetask.h sendmessagetask.h \ + getdetailstask.h getstatustask.h typingtask.h connectiontask.h sendinvitetask.h \ + joinconferencetask.h leaveconferencetask.h rejectinvitetask.h createcontacttask.h \ + modifycontactlisttask.h createfoldertask.h movecontacttask.h updateitemtask.h deleteitemtask.h \ + updatefoldertask.h updatecontacttask.h pollsearchresultstask.h privacyitemtask.h \ + needfoldertask.h searchchattask.h getchatsearchresultstask.h searchusertask.h \ + chatcountstask.h joinchattask.h + + +libgroupwise_tasks_la_LDFLAGS = -no-undefined $(all_libraries) +libgroupwise_tasks_la_LIBADD = $(LIB_QT) + + + diff --git a/kopete/protocols/groupwise/libgroupwise/tasks/chatcountstask.cpp b/kopete/protocols/groupwise/libgroupwise/tasks/chatcountstask.cpp new file mode 100644 index 00000000..9e9837f7 --- /dev/null +++ b/kopete/protocols/groupwise/libgroupwise/tasks/chatcountstask.cpp @@ -0,0 +1,87 @@ +/* + Kopete Groupwise Protocol + ChatCountsTask.cpp - Task to update chatroom participant counts + + Copyright (c) 2005 SUSE Linux Products GmbH http://www.suse.com + + Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#include <qmap.h> +#include <kdebug.h> + +#include "gwfield.h" +#include "response.h" + +#include "chatcountstask.h" + +using namespace GroupWise; + +ChatCountsTask::ChatCountsTask(Task* parent): RequestTask(parent) +{ + Field::FieldList lst; + createTransfer( "chatcounts", lst ); +} + + +ChatCountsTask::~ChatCountsTask() +{ +} + +bool ChatCountsTask::take( Transfer * transfer ) +{ + if ( !forMe( transfer ) ) + return false; + Response * response = dynamic_cast<Response *>( transfer ); + if ( !response ) + return false; + if ( response->resultCode() ) + { + setError( response->resultCode() ); + return true; + } + + Field::FieldList responseFields = response->fields(); + Field::MultiField * resultsArray = responseFields.findMultiField( NM_A_FA_RESULTS ); + if ( !resultsArray ) + { + setError( Protocol ); + return true; + } + Field::FieldList counts = resultsArray->fields(); + const Field::FieldListIterator end = counts.end(); + for ( Field::FieldListIterator it = counts.find( NM_A_FA_CHAT ); + it != end; + it = counts.find( ++it, NM_A_FA_CHAT ) ) + { + Field::MultiField * mf = static_cast<Field::MultiField *>( *it ); + Field::FieldList chat = mf->fields(); + QString roomName; + int participants; + // read the supplied fields, set metadata and status. + Field::SingleField * sf; + if ( ( sf = chat.findSingleField ( NM_A_DISPLAY_NAME ) ) ) + roomName = sf->value().toString(); + if ( ( sf = chat.findSingleField ( NM_A_UD_PARTICIPANTS ) ) ) + participants = sf->value().toInt(); + + m_results.insert( roomName, participants ); + } + return true; +} + +QMap< QString, int > ChatCountsTask::results() +{ + return m_results; +} + +#include "chatcountstask.moc" diff --git a/kopete/protocols/groupwise/libgroupwise/tasks/chatcountstask.h b/kopete/protocols/groupwise/libgroupwise/tasks/chatcountstask.h new file mode 100644 index 00000000..c80a219a --- /dev/null +++ b/kopete/protocols/groupwise/libgroupwise/tasks/chatcountstask.h @@ -0,0 +1,49 @@ +/* + Kopete Groupwise Protocol + chatcountstask.cpp - Task to update chatroom participant counts + + Copyright (c) 2005 SUSE Linux Products GmbH http://www.suse.com + + Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#ifndef CHATCOUNTSTASK_H +#define CHATCOUNTSTASK_H + +#include <qvaluelist.h> + +#include "gwerror.h" +#include "gwfield.h" + +#include "requesttask.h" + +/** +Get the current number of users in each chat on the server + +@author SUSE Linux Products GmbH + */ +class ChatCountsTask : public RequestTask +{ + Q_OBJECT + public: + ChatCountsTask(Task* parent); + ~ChatCountsTask(); + bool take( Transfer * transfer ); + /** + * Contains a list of all the chatrooms that have participants on the server. If a chatroom exists but is empty, this task does not return a result, so update the participants count to 0. + */ + QMap< QString, int > results(); + private: + QMap< QString, int > m_results; +}; + +#endif diff --git a/kopete/protocols/groupwise/libgroupwise/tasks/chatpropertiestask.cpp b/kopete/protocols/groupwise/libgroupwise/tasks/chatpropertiestask.cpp new file mode 100644 index 00000000..66b2da42 --- /dev/null +++ b/kopete/protocols/groupwise/libgroupwise/tasks/chatpropertiestask.cpp @@ -0,0 +1,139 @@ +/* + Kopete Groupwise Protocol + ChatPropertiesTask.cpp - Task to update chatroom participant counts + + Copyright (c) 2005 SUSE Linux Products GmbH http://www.suse.com + + Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#include <kdebug.h> + +#include "gwfield.h" +#include "response.h" + +#include "chatpropertiestask.h" + +using namespace GroupWise; + +ChatPropertiesTask::ChatPropertiesTask(Task* parent): RequestTask(parent) +{ +} + + +ChatPropertiesTask::~ChatPropertiesTask() +{ +} + +void ChatPropertiesTask::setChat( const QString &displayName ) +{ + Field::FieldList lst; + m_chat = displayName; + lst.append( new Field::SingleField( NM_A_DISPLAY_NAME, 0, NMFIELD_TYPE_UTF8, m_chat ) ); + createTransfer( "chatproperties", lst ); +} + +bool ChatPropertiesTask::take( Transfer * transfer ) +{ + if ( !forMe( transfer ) ) + return false; + Response * response = dynamic_cast<Response *>( transfer ); + if ( !response ) + return false; + if ( response->resultCode() ) + { + setError( response->resultCode() ); + return true; + } + + Field::FieldList responseFields = response->fields(); + Field::MultiField * resultsArray = responseFields.findMultiField( NM_A_FA_CHAT ); + if ( !resultsArray ) + { + setError( Protocol ); + return true; + } + + Field::FieldList lst = resultsArray->fields(); + const Field::FieldListIterator end = lst.end(); + for ( Field::FieldListIterator it = lst.begin(); + it != end; + ++it ) + { + Field::SingleField * sf = dynamic_cast<Field::SingleField *>( *it ); + if ( sf ) + { + if ( sf->tag() == NM_A_DISPLAY_NAME ) + continue; + else if ( sf->tag() == NM_A_CHAT_OWNER_DN ) + m_ownerDn = sf->value().toString(); + else if ( sf->tag() == NM_A_CHAT_CREATOR_DN ) + m_creatorDn= sf->value().toString(); + else if ( sf->tag() == NM_A_DESCRIPTION ) + m_description = sf->value().toString(); + else if ( sf->tag() == NM_A_DISCLAIMER ) + m_disclaimer = sf->value().toString(); + else if ( sf->tag() == NM_A_QUERY ) + m_query = sf->value().toString(); + else if ( sf->tag() == NM_A_ARCHIVE ) + m_archive = sf->value().toString(); + else if ( sf->tag() == NM_A_SZ_TOPIC ) + m_topic = sf->value().toString(); + else if ( sf->tag() == NM_A_CREATION_TIME ) + m_creationTime.setTime_t( sf->value().toInt() ); + else if ( sf->tag() == NM_A_UD_CHAT_RIGHTS ) + m_rights = sf->value().toInt(); + + } + else + { + Field::MultiField * mf = dynamic_cast<Field::MultiField *>( *it ); + if ( mf ) + { + if ( mf->tag() == NM_A_FA_CHAT_ACL ) + { + Field::FieldList acl = mf->fields(); + const Field::FieldListIterator aclEnd = acl.end(); + for ( Field::FieldListIterator aclIt = acl.begin(); + aclIt != aclEnd; + ++aclIt ) + { + Field::MultiField * aclEntryFields = dynamic_cast<Field::MultiField *>( *aclIt ); + if ( aclEntryFields ) + { + ChatContact entry; + Field::FieldList entryFields = aclEntryFields->fields(); + Field::SingleField * sf; + if ( ( sf = entryFields.findSingleField ( NM_A_SZ_DN ) ) ) + entry.dn = sf->value().toString(); + if ( ( sf = entryFields.findSingleField ( NM_A_SZ_ACCESS_FLAGS ) ) ) + entry.chatRights = sf->value().toInt(); + kdDebug ( GROUPWISE_DEBUG_GLOBAL ) << "got acl entry: " << entry.dn << ", " << entry.chatRights << endl; + m_aclEntries.append( entry ); + } + + } + } + } + } + } + kdDebug ( GROUPWISE_DEBUG_GLOBAL ) << "Got chatroom properties: " << m_chat << " : " << m_ownerDn << ", " << m_description << ", " << m_disclaimer << ", " << m_query << ", " << m_archive << ", " << m_topic << ", " << m_creatorDn << ", " << m_creationTime.toString() << ", " << m_rights << endl; + finished(); + return true; +} + +QValueList< ChatContact > ChatPropertiesTask::aclEntries() +{ + return m_aclEntries; +} + +#include "chatpropertiestask.moc" diff --git a/kopete/protocols/groupwise/libgroupwise/tasks/chatpropertiestask.h b/kopete/protocols/groupwise/libgroupwise/tasks/chatpropertiestask.h new file mode 100644 index 00000000..c9f890dd --- /dev/null +++ b/kopete/protocols/groupwise/libgroupwise/tasks/chatpropertiestask.h @@ -0,0 +1,64 @@ +/* + Kopete Groupwise Protocol + chatcountstask.cpp - Task to update chatroom participant counts + + Copyright (c) 2005 SUSE Linux Products GmbH http://www.suse.com + + Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#ifndef CHATPROPERTIESTASK_H +#define CHATPROPERTIESTASK_H + +#include <qdatetime.h> +#include <qvaluelist.h> +#include "gwchatrooms.h" +#include "gwerror.h" +#include "gwfield.h" + +#include "requesttask.h" + +/** +Get the current number of users in each chat on the server + +@author SUSE Linux Products GmbH + */ +class ChatPropertiesTask : public RequestTask +{ + Q_OBJECT + public: + ChatPropertiesTask(Task* parent); + ~ChatPropertiesTask(); + /** + * Specify which chatroom to get properties for + */ + void setChat( const QString & ); + bool take( Transfer * transfer ); + /** + * Contains a list of the ACL entries for the specified chatroom + */ + QValueList< GroupWise::ChatContact > aclEntries(); + QString m_chat; + QString m_ownerDn; + QString m_description; + QString m_disclaimer; + QString m_query; + QString m_archive; + QString m_maxUsers; + QString m_topic; + QString m_creatorDn; + QDateTime m_creationTime; + uint m_rights; + QValueList< GroupWise::ChatContact > m_aclEntries; +}; + +#endif diff --git a/kopete/protocols/groupwise/libgroupwise/tasks/conferencetask.cpp b/kopete/protocols/groupwise/libgroupwise/tasks/conferencetask.cpp new file mode 100644 index 00000000..9773a622 --- /dev/null +++ b/kopete/protocols/groupwise/libgroupwise/tasks/conferencetask.cpp @@ -0,0 +1,230 @@ +/* + Kopete Groupwise Protocol + conferencetask.cpp - Event Handling task responsible for all conference related events + + Copyright (c) 2004 SUSE Linux AG http://www.suse.com + + Based on Iris, Copyright (C) 2003 Justin Karneges + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#include "client.h" +#include "userdetailsmanager.h" + +#include "conferencetask.h" + +ConferenceTask::ConferenceTask( Task* parent ) + : EventTask( parent ) +{ + // register all the events that this task monitors + registerEvent( GroupWise::ConferenceClosed ); + registerEvent( GroupWise::ConferenceJoined ); + registerEvent( GroupWise::ConferenceLeft ); + registerEvent( GroupWise::ReceiveMessage ); + registerEvent( GroupWise::UserTyping ); + registerEvent( GroupWise::UserNotTyping ); + registerEvent( GroupWise::ConferenceInvite ); + registerEvent( GroupWise::ConferenceInviteNotify ); + registerEvent( GroupWise::ConferenceReject ); + registerEvent( GroupWise::ReceiveAutoReply ); + // GW7 + registerEvent( GroupWise::ReceivedBroadcast ); + registerEvent( GroupWise::ReceivedSystemBroadcast ); + + // listen to the UserDetailsManager telling us that user details are available + connect( client()->userDetailsManager(), SIGNAL( gotContactDetails( const GroupWise::ContactDetails & ) ), + SLOT( slotReceiveUserDetails( const GroupWise::ContactDetails & ) ) ); +} + + +ConferenceTask::~ConferenceTask() +{ +} + +void ConferenceTask::dumpConferenceEvent( ConferenceEvent & evt ) +{ + client()->debug( QString( "Conference Event - guid: %1 user: %2 timestamp: %3:%4:%5" ).arg + ( evt.guid ).arg( evt.user.ascii() ).arg( evt.timeStamp.time().hour() ).arg + ( evt.timeStamp.time().minute() ).arg( evt.timeStamp.time().second() ) ); + client()->debug( QString( " flags: %1" ).arg( evt.flags, 8 ) ); +} + +bool ConferenceTask::take( Transfer * transfer ) +{ + EventTransfer * incomingEvent; + if ( forMe( transfer, incomingEvent ) ) + { + client()->debug( "Got a conference event:" ); + ConferenceEvent event; + event.type = (GroupWise::Event)( incomingEvent->eventType() ); + event.timeStamp = incomingEvent->timeStamp(); + event.user = incomingEvent->source(); + event.flags = 0; + Q_ASSERT( incomingEvent->hasGuid() ); + event.guid = incomingEvent->guid(); + + switch ( event.type ) + { + case GroupWise::ConferenceClosed: + // extra debug - we never see these events, against spec. + client()->debug( "********************" ); + client()->debug( "* ConferenceClosed *" ); + client()->debug( "* ConferenceClosed *" ); + client()->debug( "* ConferenceClosed *" ); + client()->debug( "********************" ); + emit closed( event ); + break; + case GroupWise::ConferenceJoined: + Q_ASSERT( incomingEvent->hasFlags() ); + event.flags = incomingEvent->flags(); + client()->debug( "ConferenceJoined" ); + if ( !queueWhileAwaitingData( event ) ) + emit joined( event ); + break; + case GroupWise::ConferenceLeft: + Q_ASSERT( incomingEvent->hasFlags() ); + event.flags = incomingEvent->flags(); + client()->debug( "ConferenceLeft" ); + emit left( event ); + break; + case GroupWise::ReceiveMessage: + Q_ASSERT( incomingEvent->hasFlags() ); + event.flags = incomingEvent->flags(); + Q_ASSERT( incomingEvent->hasMessage() ); + event.message = incomingEvent->message(); + client()->debug( "ReceiveMessage" ); + client()->debug( QString( "message: %1" ).arg( event.message ) ); + if ( !queueWhileAwaitingData( event ) ) + emit message( event ); + break; + case GroupWise::UserTyping: + client()->debug( "UserTyping" ); + emit typing( event ); + break; + case GroupWise::UserNotTyping: + client()->debug( "UserNotTyping" ); + emit notTyping( event ); + break; + case GroupWise::ConferenceInvite: + Q_ASSERT( incomingEvent->hasMessage() ); + event.message = incomingEvent->message(); + client()->debug( "ConferenceInvite" ); + client()->debug( QString( "message: %1" ).arg( event.message ) ); + if ( !queueWhileAwaitingData( event ) ) + emit invited( event ); + break; + case GroupWise::ConferenceInviteNotify: + client()->debug( "ConferenceInviteNotify" ); + if ( !queueWhileAwaitingData( event ) ) + emit otherInvited( event ); + break; + case GroupWise::ConferenceReject: + client()->debug( "ConferenceReject" ); + if ( !queueWhileAwaitingData( event ) ) + emit invitationDeclined( event ); + break; + case GroupWise::ReceiveAutoReply: + Q_ASSERT( incomingEvent->hasFlags() ); + event.flags = incomingEvent->flags(); + Q_ASSERT( incomingEvent->hasMessage() ); + event.message = incomingEvent->message(); + client()->debug( "ReceiveAutoReply" ); + client()->debug( QString( "message: %1" ).arg( event.message.ascii() ) ); + emit autoReply( event ); + break; + case GroupWise::ReceivedBroadcast: + Q_ASSERT( incomingEvent->hasMessage() ); + event.message = incomingEvent->message(); + client()->debug( "ReceivedBroadCast" ); + client()->debug( QString( "message: %1" ).arg( event.message ) ); + if ( !queueWhileAwaitingData( event ) ) + emit broadcast( event ); + break; + case GroupWise::ReceivedSystemBroadcast: + Q_ASSERT( incomingEvent->hasMessage() ); + event.message = incomingEvent->message(); + client()->debug( "ReceivedSystemBroadCast" ); + client()->debug( QString( "message: %1" ).arg( event.message ) ); + emit systemBroadcast( event ); + break; + default: + client()->debug( QString( "WARNING: didn't handle registered event %1, on conference %2" ).arg( incomingEvent->eventType() ).arg( event.guid.ascii() ) ); + } + dumpConferenceEvent( event ); + + return true; + } + return false; +} + +void ConferenceTask::slotReceiveUserDetails( const GroupWise::ContactDetails & details ) +{ + client()->debug( "ConferenceTask::slotReceiveUserDetails()" ); + + // dequeue any events which are deliverable now we have these details + QValueListIterator< ConferenceEvent > end = m_pendingEvents.end(); + QValueListIterator< ConferenceEvent > it = m_pendingEvents.begin(); + while ( it != end ) + { + QValueListIterator< ConferenceEvent > current = it; + ++it; + // if the details relate to event, try again to handle it + if ( details.dn == (*current).user ) + { + client()->debug( QString( " - got details for event involving %1" ).arg( (*current).user ) ); + switch ( (*current).type ) + { + case GroupWise::ConferenceJoined: + client()->debug( "ConferenceJoined" ); + emit joined( *current ); + break; + case GroupWise::ReceiveMessage: + client()->debug( "ReceiveMessage" ); + emit message( *current ); + break; + case GroupWise::ConferenceInvite: + client()->debug( "ConferenceInvite" ); + emit invited( *current ); + break; + case GroupWise::ConferenceInviteNotify: + client()->debug( "ConferenceInviteNotify" ); + emit otherInvited( *current ); + break; + default: + client()->debug( "Queued an event while waiting for more data, but didn't write a handler for the dequeue!" ); + } + m_pendingEvents.remove( current ); + client()->debug( QString( "Event handled - now %1 pending events" ).arg + ( (uint)m_pendingEvents.count() ) ); + } + } +} + + +bool ConferenceTask::queueWhileAwaitingData( const ConferenceEvent & event ) +{ + if ( client()->userDetailsManager()->known( event.user ) ) + { + client()->debug( "ConferenceTask::queueWhileAwaitingData() - source is known!" ); + return false; + } + else + { + client()->debug( QString( "ConferenceTask::queueWhileAwaitingData() - queueing event involving %1" ).arg( event.user ) ); + client()->userDetailsManager()->requestDetails( event.user ); + m_pendingEvents.append( event ); + return true; + } +} + +#include "conferencetask.moc" diff --git a/kopete/protocols/groupwise/libgroupwise/tasks/conferencetask.h b/kopete/protocols/groupwise/libgroupwise/tasks/conferencetask.h new file mode 100644 index 00000000..42f4fc2b --- /dev/null +++ b/kopete/protocols/groupwise/libgroupwise/tasks/conferencetask.h @@ -0,0 +1,74 @@ +/* + Kopete Groupwise Protocol + conferencetask.h - Event Handling task responsible for all conference related events + + Copyright (c) 2004 SUSE Linux AG http://www.suse.com + + Based on Iris, Copyright (C) 2003 Justin Karneges + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#ifndef CONFERENCETASK_H +#define CONFERENCETASK_H + +#include "gwerror.h" +#include "eventtask.h" + +/** + * This Task is responsible for handling all conference related events, and signalling them up to @ref GroupWiseAccount + * Implementation note: It would be fit the model more cleanly to have each of these in their own Task, but the amount + * of code they share is quite large, and the differences in the way each event uses it are small + * @author SUSE AG + */ + +using namespace GroupWise; + +class ConferenceTask : public EventTask +{ +Q_OBJECT +public: + ConferenceTask( Task* parent ); + ~ConferenceTask(); + bool take( Transfer * transfer ); +signals: + void typing( const ConferenceEvent & ); + void notTyping( const ConferenceEvent & ); + void joined( const ConferenceEvent & ); + void left( const ConferenceEvent &); + void invited( const ConferenceEvent & ); + void otherInvited( const ConferenceEvent & ); + void invitationDeclined( const ConferenceEvent & ); + void closed( const ConferenceEvent & ); + void message( const ConferenceEvent &); + void autoReply( const ConferenceEvent & ); + // GW7 + void broadcast( const ConferenceEvent &); + void systemBroadcast( const ConferenceEvent &); +protected slots: + void slotReceiveUserDetails( const GroupWise::ContactDetails & ); +protected: + Q_UINT32 readFlags( QDataStream & din ); + QString readMessage( QDataStream & din ); + /** + * Checks to see if we need more data from the client before we can propagate this event + * and queues the event if so + * @return whether the event was queued pending more data + */ + bool queueWhileAwaitingData( const ConferenceEvent & event ); + void dumpConferenceEvent( ConferenceEvent & evt ); +private: + // A list of events which are waiting for more data from the server before they can be exposed to the client + QValueList< ConferenceEvent > m_pendingEvents; +}; + +#endif diff --git a/kopete/protocols/groupwise/libgroupwise/tasks/connectiontask.cpp b/kopete/protocols/groupwise/libgroupwise/tasks/connectiontask.cpp new file mode 100644 index 00000000..3d041208 --- /dev/null +++ b/kopete/protocols/groupwise/libgroupwise/tasks/connectiontask.cpp @@ -0,0 +1,55 @@ +/* + Kopete Groupwise Protocol + connectiontask.cpp - Event Handling task responsible for all connection related events + + Copyright (c) 2004 SUSE Linux AG http://www.suse.com + + Based on Iris, Copyright (C) 2003 Justin Karneges + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ +#include "client.h" + +#include "connectiontask.h" + +ConnectionTask::ConnectionTask(Task* parent): EventTask(parent) +{ + registerEvent( GroupWise::UserDisconnect ); + registerEvent( GroupWise::ServerDisconnect ); +} + + +ConnectionTask::~ConnectionTask() +{ +} + +bool ConnectionTask::take( Transfer * transfer ) +{ + EventTransfer * incomingEvent; + if ( forMe( transfer, incomingEvent ) ) + { + client()->debug( "Got a connection event:" ); + switch ( incomingEvent->eventType() ) + { + case GroupWise::UserDisconnect: + emit connectedElsewhere(); + break; + case GroupWise::ServerDisconnect: + emit serverDisconnect(); + break; + } + return true; + } + return false; +} + +#include "connectiontask.moc" diff --git a/kopete/protocols/groupwise/libgroupwise/tasks/connectiontask.h b/kopete/protocols/groupwise/libgroupwise/tasks/connectiontask.h new file mode 100644 index 00000000..95df34f9 --- /dev/null +++ b/kopete/protocols/groupwise/libgroupwise/tasks/connectiontask.h @@ -0,0 +1,43 @@ +/* + Kopete Groupwise Protocol + connectiontask.h - Event Handling task responsible for all connection related events + + Copyright (c) 2004 SUSE Linux AG http://www.suse.com + + Based on Iris, Copyright (C) 2003 Justin Karneges + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#ifndef CONNECTIONTASK_H +#define CONNECTIONTASK_H + +#include "eventtask.h" + +/** +This task monitors connection related events, currently 'connected elsewhere' disconnects and server disconnect notification. + +@author Kopete Developers +*/ +class ConnectionTask : public EventTask +{ +Q_OBJECT +public: + ConnectionTask(Task* parent); + ~ConnectionTask(); + bool take( Transfer * transfer ); +signals: + void connectedElsewhere(); + void serverDisconnect(); +}; + +#endif diff --git a/kopete/protocols/groupwise/libgroupwise/tasks/createconferencetask.cpp b/kopete/protocols/groupwise/libgroupwise/tasks/createconferencetask.cpp new file mode 100644 index 00000000..8be16888 --- /dev/null +++ b/kopete/protocols/groupwise/libgroupwise/tasks/createconferencetask.cpp @@ -0,0 +1,85 @@ +/* + Kopete Groupwise Protocol + createconferencetask.cpp - Request task that creates conferences on the server + + Copyright (c) 2004 SUSE Linux AG http://www.suse.com + + Based on Iris, Copyright (C) 2003 Justin Karneges + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#include "client.h" +#include "response.h" + + +#include "createconferencetask.h" + +CreateConferenceTask::CreateConferenceTask(Task* parent): RequestTask(parent), m_confId( 0 ), m_guid( BLANK_GUID ) +{ + +} + +CreateConferenceTask::~CreateConferenceTask() +{ +} + +void CreateConferenceTask::conference( const int confId, const QStringList &participants ) +{ + m_confId = confId; + Field::FieldList lst, tmp; + // list containing blank GUID + tmp.append( new Field::SingleField( NM_A_SZ_OBJECT_ID, 0, NMFIELD_TYPE_UTF8, m_guid ) ); + lst.append( new Field::MultiField( NM_A_FA_CONVERSATION, NMFIELD_METHOD_VALID, 0, NMFIELD_TYPE_ARRAY, tmp ) ); + // series of participants (may be empty ) + QValueListConstIterator<QString> end = participants.end(); + for ( QValueListConstIterator<QString> it = participants.begin(); it != end; ++it ) + lst.append( new Field::SingleField( NM_A_SZ_DN, 0, NMFIELD_TYPE_DN, *it ) ); + lst.append( new Field::SingleField( NM_A_SZ_DN, 0, NMFIELD_TYPE_DN, client()->userDN() ) ); + createTransfer( "createconf", lst ); +} + +bool CreateConferenceTask::take( Transfer * transfer ) +{ + if ( !forMe( transfer ) ) + return false; + Response * response = dynamic_cast<Response *>( transfer ); + if ( !response ) + return false; + + // if the createconf was successful, read the GUID and store it + Field::FieldList responseFields = response->fields(); + if ( response->resultCode() == GroupWise::None ) + { + Field::MultiField * listField = responseFields.findMultiField( NM_A_FA_CONVERSATION ); + Field::FieldList guidList = listField->fields(); + Field::SingleField * guidField = guidList.findSingleField( NM_A_SZ_OBJECT_ID ); + m_guid = guidField->value().toString(); + setSuccess(); + } + else + setError( response->resultCode() ); + return true; + +} + +GroupWise::ConferenceGuid CreateConferenceTask::conferenceGUID() const +{ + return m_guid; +} + +int CreateConferenceTask::clientConfId() const +{ + return m_confId; +} + +#include "createconferencetask.moc" diff --git a/kopete/protocols/groupwise/libgroupwise/tasks/createconferencetask.h b/kopete/protocols/groupwise/libgroupwise/tasks/createconferencetask.h new file mode 100644 index 00000000..48d5702e --- /dev/null +++ b/kopete/protocols/groupwise/libgroupwise/tasks/createconferencetask.h @@ -0,0 +1,54 @@ +/* + Kopete Groupwise Protocol + createconferencetask.h - Request task that creates conferences on the server + + Copyright (c) 2004 SUSE Linux AG http://www.suse.com + + Based on Iris, Copyright (C) 2003 Justin Karneges + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#ifndef CREATECONFERENCETASK_H +#define CREATECONFERENCETASK_H + +#include "requesttask.h" + +/** +This task is responsible for creating a conference at the server, and confirming that the server allowed the conference to be created. + +@author SUSE AG +*/ +class CreateConferenceTask : public RequestTask +{ +Q_OBJECT +public: + CreateConferenceTask(Task* parent); + ~CreateConferenceTask(); + /** + * Set up a create conference request + * @param confId The client-unique conference Id. + * @param participants A list of Novell DNs of the people taking part in the conference. + */ + void conference( const int confId, const QStringList &participants ); + bool take( Transfer * transfer ); + int clientConfId() const; + GroupWise::ConferenceGuid conferenceGUID() const; + +signals: + void created( const GroupWise::ConferenceGuid & guid ); +private: + int m_confId; // the conference id given us before making the request + ConferenceGuid m_guid; +}; + +#endif diff --git a/kopete/protocols/groupwise/libgroupwise/tasks/createcontactinstancetask.cpp b/kopete/protocols/groupwise/libgroupwise/tasks/createcontactinstancetask.cpp new file mode 100644 index 00000000..832b5900 --- /dev/null +++ b/kopete/protocols/groupwise/libgroupwise/tasks/createcontactinstancetask.cpp @@ -0,0 +1,97 @@ +/* + Kopete Groupwise Protocol + createcontactinstancetask.h - Request Task that creates an instance of a contact on the server side contact list + + Copyright (c) 2004 SUSE Linux AG http://www.suse.com + + Based on Iris, Copyright (C) 2003 Justin Karneges + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ +#include "client.h" + +#include "createcontactinstancetask.h" + +CreateContactInstanceTask::CreateContactInstanceTask(Task* parent) : NeedFolderTask(parent) +{ + // make the client tell the client app (Kopete) when we receive a contact + connect( this, SIGNAL( gotContactAdded( const ContactItem & ) ), client(), SIGNAL( contactReceived( const ContactItem & ) ) ); +} + +CreateContactInstanceTask::~CreateContactInstanceTask() +{ +} + +void CreateContactInstanceTask::contactFromUserId( const QString & userId, const QString & displayName, const int parentFolder ) +{ + contact( new Field::SingleField( NM_A_SZ_USERID, 0, NMFIELD_TYPE_UTF8, userId ), displayName, parentFolder ); +} + +void CreateContactInstanceTask::contactFromUserIdAndFolder( const QString & userId, const QString & displayName, const int folderSequence, const QString & folderDisplayName ) +{ + // record the user details + m_userId = userId; + m_displayName = displayName; + // record the folder details + m_folderSequence = folderSequence; + m_folderDisplayName = folderDisplayName; +} + +void CreateContactInstanceTask::contactFromDN( const QString & dn, const QString & displayName, const int parentFolder ) +{ + contact( new Field::SingleField( NM_A_SZ_DN, 0, NMFIELD_TYPE_UTF8, dn ), displayName, parentFolder ); +} + +void CreateContactInstanceTask::contactFromDNAndFolder( const QString & dn, const QString & displayName, const int folderSequence, const QString & folderDisplayName ) +{ + // record the user details + m_dn = dn; + m_displayName = displayName; + // record the folder details + m_folderSequence = folderSequence; + m_folderDisplayName = folderDisplayName; +} + +void CreateContactInstanceTask::contact( Field::SingleField * id, const QString & displayName, const int parentFolder ) +{ + Field::FieldList lst; + lst.append( new Field::SingleField( NM_A_SZ_PARENT_ID, 0, NMFIELD_TYPE_UTF8, QString::number( parentFolder ) ) ); + // this is either a user Id or a DN + lst.append( id ); + if ( displayName.isEmpty() ) // fallback so that the contact is created + lst.append( new Field::SingleField( NM_A_SZ_DISPLAY_NAME, 0, NMFIELD_TYPE_UTF8, m_dn ) ); + else + lst.append( new Field::SingleField( NM_A_SZ_DISPLAY_NAME, 0, NMFIELD_TYPE_UTF8, displayName ) ); + createTransfer( "createcontact", lst ); +} + +void CreateContactInstanceTask::onGo() +{ + // are we creating a folder first or can we just proceed as normal? + if ( m_folderDisplayName.isEmpty() ) + RequestTask::onGo(); + else // create the folder, when the folder has been created, onFolderCreated gets called and creates the contact + createFolder(); +} + +void CreateContactInstanceTask::onFolderCreated() +{ + // now the folder exists, perform the requested type of contact instance creation + if ( m_userId.isEmpty() ) + contact( new Field::SingleField( NM_A_SZ_DN, 0, NMFIELD_TYPE_UTF8, m_dn ), m_displayName, m_folderId ); + else + contact( new Field::SingleField( NM_A_SZ_USERID, 0, NMFIELD_TYPE_UTF8, m_userId ), m_displayName, m_folderId ); + // send the transfer immediately + RequestTask::onGo(); +} + +#include "createcontactinstancetask.moc" diff --git a/kopete/protocols/groupwise/libgroupwise/tasks/createcontactinstancetask.h b/kopete/protocols/groupwise/libgroupwise/tasks/createcontactinstancetask.h new file mode 100644 index 00000000..d6be5933 --- /dev/null +++ b/kopete/protocols/groupwise/libgroupwise/tasks/createcontactinstancetask.h @@ -0,0 +1,54 @@ +/* + Kopete Groupwise Protocol + createcontactinstancetask.h - Request Task that creates an instance of a contact on the server side contact list + + Copyright (c) 2004 SUSE Linux AG http://www.suse.com + + Based on Iris, Copyright (C) 2003 Justin Karneges + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#ifndef CreateContactInstanceTask_H +#define CreateContactInstanceTask_H + +#include "needfoldertask.h" + +/** +Creates a contact on the server. The response to this action is handled by its parent + +@author SUSE AG +*/ +class CreateContactInstanceTask : public NeedFolderTask +{ +Q_OBJECT +public: + CreateContactInstanceTask(Task* parent); + ~CreateContactInstanceTask(); + /** + * Sets up the request message. + */ + void contactFromUserId( const QString & userId, const QString & displayName, const int parentFolder ); + void contactFromDN( const QString & dn, const QString & displayName, const int parentFolder ); + void contactFromUserIdAndFolder( const QString & userId, const QString & displayName, const int folderSequence, const QString & folderDisplayName ); + void contactFromDNAndFolder( const QString & dn, const QString & displayName, const int folderSequence, const QString & folderDisplayName ); + void onGo(); +protected: + void contact( Field::SingleField * id, const QString & displayName, const int parentFolder ); + void onFolderCreated(); +private: + QString m_userId; + QString m_dn; + QString m_displayName; +}; + +#endif diff --git a/kopete/protocols/groupwise/libgroupwise/tasks/createcontacttask.cpp b/kopete/protocols/groupwise/libgroupwise/tasks/createcontacttask.cpp new file mode 100644 index 00000000..aac16042 --- /dev/null +++ b/kopete/protocols/groupwise/libgroupwise/tasks/createcontacttask.cpp @@ -0,0 +1,144 @@ +/* + Kopete Groupwise Protocol + createcontacttask.cpp - high level task responsible for creating both a contact and any folders it belongs to locally, on the server + + Copyright (c) 2004 SUSE Linux AG http://www.suse.com + + Based on Iris, Copyright (C) 2003 Justin Karneges + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#include "client.h" +#include "createfoldertask.h" +#include "createcontactinstancetask.h" + +#include "createcontacttask.h" + +CreateContactTask::CreateContactTask(Task* parent): Task(parent) +{ +} + +CreateContactTask::~CreateContactTask() +{ +} + +QString CreateContactTask::userId() +{ + return m_userId; +} + +QString CreateContactTask::dn() +{ + return m_dn; +} + +QString CreateContactTask::displayName() +{ + return m_displayName; +} + +bool CreateContactTask::take( Transfer * transfer ) +{ + Q_UNUSED( transfer ); + return false; +} + +void CreateContactTask::contactFromUserId( const QString & userId, const QString & displayName, const int firstSeqNo, const QValueList< FolderItem > folders, bool topLevel ) +{ + m_userId = userId; + m_displayName = displayName; + m_firstSequenceNumber = firstSeqNo; + m_folders = folders; + m_topLevel = topLevel; +} + +void CreateContactTask::onGo() +{ + client()->debug( "CreateContactTask::onGo() - Welcome to the Create Contact Task Show!"); + QValueList<FolderItem>::ConstIterator it = m_folders.begin(); + const QValueList<FolderItem>::ConstIterator end = m_folders.end(); + + // create contacts on the server + for ( ; it != end; ++it ) + { + client()->debug( QString( " - contact is in folder %1 with id %2" ).arg( (*it).name ).arg( (*it).id ) ); + CreateContactInstanceTask * ccit = new CreateContactInstanceTask( client()->rootTask() ); + // the add contact action may cause other contacts' sequence numbers to change + // CreateContactInstanceTask signals these changes, so we propagate the signal via the Client, to the GroupWiseAccount + // This updates our local versions of those contacts using the same mechanism by which they are updated at login. + connect( ccit, SIGNAL( gotContactAdded( const ContactItem & ) ), SLOT( slotContactAdded( const ContactItem & ) ) ); + connect( ccit, SIGNAL( finished() ), SLOT( slotCheckContactInstanceCreated() ) ); + if ( (*it).id == 0 ) // caller asserts that this isn't on the server... + { + ccit->contactFromDNAndFolder( m_userId, m_displayName, m_firstSequenceNumber++, ( *it ).name ); + } + else + ccit->contactFromDN( m_userId, m_displayName, (*it).id ); + + ccit->go( true ); + } + + if ( m_topLevel ) + { + client()->debug( " - contact is in top level folder " ); + CreateContactInstanceTask * ccit = new CreateContactInstanceTask( client()->rootTask() ); + connect( ccit, SIGNAL( gotContactAdded( const ContactItem & ) ), SLOT( slotContactAdded( const ContactItem & ) ) ); + connect( ccit, SIGNAL( finished() ), SLOT( slotCheckContactInstanceCreated() ) ); + ccit->contactFromDN( m_userId, m_displayName, 0 ); + ccit->go( true ); + } + client()->debug( "CreateContactTask::onGo() - DONE" ); +} + +void CreateContactTask::slotContactAdded( const ContactItem & addedContact ) +{ + client()->debug( "CreateContactTask::slotContactAdded()" ); + // as each contact instance has been added on the server, + // remove the folderitem it belongs in. + // once the list is empty, we have been successful + + if ( addedContact.displayName != m_displayName ) + { + client()->debug( " - addedContact is not the one we were trying to add, ignoring it ( Account will update it )" ); + return; + } + client()->debug( QString( "CreateContactTask::slotContactAdded() - Contact Instance %1 was created on the server, with objectId %2 in folder %3" ).arg + ( addedContact.displayName ).arg( addedContact.id ).arg( addedContact.parentId ) ); + + if ( m_dn.isEmpty() ) + m_dn = addedContact.dn; + + + if ( !m_folders.isEmpty() ) + m_folders.pop_back(); + + // clear the topLevel flag once the corresponding server side entry has been successfully created + if ( addedContact.parentId == 0 ) + m_topLevel = false; + + if ( m_folders.isEmpty() && !m_topLevel ) + { + client()->debug( "CreateContactTask::slotContactAdded() - All contacts were created on the server, we're finished!" ); + setSuccess(); + } +} +void CreateContactTask::slotCheckContactInstanceCreated() +{ + CreateContactInstanceTask * ccit = ( CreateContactInstanceTask * )sender(); + if ( !ccit->success() ) + { + setError( ccit->statusCode(), ccit->statusString() ); + } +} + +#include "createcontacttask.moc" diff --git a/kopete/protocols/groupwise/libgroupwise/tasks/createcontacttask.h b/kopete/protocols/groupwise/libgroupwise/tasks/createcontacttask.h new file mode 100644 index 00000000..a9e4ab06 --- /dev/null +++ b/kopete/protocols/groupwise/libgroupwise/tasks/createcontacttask.h @@ -0,0 +1,88 @@ +/* + Kopete Groupwise Protocol + createcontacttask.cpp - high level task responsible for creating both a contact and any folders it belongs to locally, on the server + + Copyright (c) 2004 SUSE Linux AG http://www.suse.com + + Based on Iris, Copyright (C) 2003 Justin Karneges + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#ifndef CREATECONTACTTASK_H +#define CREATECONTACTTASK_H + +#include <qvaluelist.h> + +#include "gwerror.h" + +#include "task.h" + +using namespace GroupWise; + +/** + Creates a contact on the server, as well as any folders that do not exist on the server, and add the contact to those folders. + This is a meta-task to suit Kopete. If you maintain your own copy of the server side contact list and follow the server's + contact semantics (contact instances rather than contacts in the contact list), you can just use CreateContactInstanceTask. + This task causes the @ref Client to emit folderReceived() and contactReceived() as the task proceeds. Kopete processes these + signals as usual, because it created the contact optimistically, before invoking this task. + + The finished() signal indicates the whole procedure has completed and the sender can be queried for success as usual +@author SUSE AG +*/ +class CreateContactTask : public Task +{ +Q_OBJECT +public: + CreateContactTask(Task* parent); + ~CreateContactTask(); + /** + * Get the userId of the contact just created + */ + QString userId(); + /** + * Get the DN of the contact just created + */ + QString dn(); + QString displayName(); + + /** + * Sets up the task. + * @param userId the user Id of the contact to create + * @param displayName the display name we should give to this contact + * @param firstSeqNo Used to create the folders - the first unused folder sequence number we know of + * @param folders A list of folders that the contact should belong to - any folders that do not exist on the server should have a objectId of 0, and will be created + * @param topLevel is the folder also in the top level folder? + */ + void contactFromUserId( const QString & userId, const QString & displayName, const int firstSeqNo, const QValueList< FolderItem > folders, bool topLevel ); + //void contactFromDN( const QString & dn, const QString & displayName, const int parentFolder ); + /** + * This task doesn't do any I/O itself, so this take prints an error and returns false; + */ + bool take( Transfer * ); + /** + * Starts off the whole process + */ + void onGo(); +protected slots: + void slotContactAdded( const ContactItem & ); + void slotCheckContactInstanceCreated(); +private: + int m_firstSequenceNumber; + QString m_userId; + QString m_dn; + QString m_displayName; + QValueList< FolderItem > m_folders; + bool m_topLevel; +}; + +#endif diff --git a/kopete/protocols/groupwise/libgroupwise/tasks/createfoldertask.cpp b/kopete/protocols/groupwise/libgroupwise/tasks/createfoldertask.cpp new file mode 100644 index 00000000..c7e9933b --- /dev/null +++ b/kopete/protocols/groupwise/libgroupwise/tasks/createfoldertask.cpp @@ -0,0 +1,41 @@ +/* + Kopete Groupwise Protocol + createfoldertask.h - Request Task for creating a single folder on the server + + Copyright (c) 2004 SUSE Linux AG http://www.suse.com + + Based on Iris, Copyright (C) 2003 Justin Karneges + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#include "createfoldertask.h" + +CreateFolderTask::CreateFolderTask(Task* parent): ModifyContactListTask(parent) +{ +} + + +CreateFolderTask::~CreateFolderTask() +{ +} + +void CreateFolderTask::folder( const int parentId, const int sequence, const QString & displayName ) +{ + Field::FieldList lst; + lst.append( new Field::SingleField( NM_A_SZ_PARENT_ID, 0, NMFIELD_TYPE_UTF8, QString::number( parentId ) ) ); + lst.append( new Field::SingleField( NM_A_SZ_DISPLAY_NAME, 0, NMFIELD_TYPE_UTF8, displayName ) ); + lst.append( new Field::SingleField( NM_A_SZ_SEQUENCE_NUMBER, 0, NMFIELD_TYPE_UTF8, QString::number( sequence ) ) ); + createTransfer( "createfolder", lst ); +} + +#include "createfoldertask.moc" diff --git a/kopete/protocols/groupwise/libgroupwise/tasks/createfoldertask.h b/kopete/protocols/groupwise/libgroupwise/tasks/createfoldertask.h new file mode 100644 index 00000000..f3c6ebb9 --- /dev/null +++ b/kopete/protocols/groupwise/libgroupwise/tasks/createfoldertask.h @@ -0,0 +1,40 @@ +/* + Kopete Groupwise Protocol + createfoldertask.h - Request Task for creating a single folder on the server + + Copyright (c) 2004 SUSE Linux AG http://www.suse.com + + Based on Iris, Copyright (C) 2003 Justin Karneges + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#ifndef CREATEFOLDERTASK_H +#define CREATEFOLDERTASK_H + +#include "modifycontactlisttask.h" + +/** +Creates a folder on the server + +@author SUSE AG +*/ +class CreateFolderTask : public ModifyContactListTask +{ +Q_OBJECT +public: + CreateFolderTask(Task* parent); + ~CreateFolderTask(); + void folder( const int parentId, const int sequence, const QString & displayName ); +}; + +#endif diff --git a/kopete/protocols/groupwise/libgroupwise/tasks/deleteitemtask.cpp b/kopete/protocols/groupwise/libgroupwise/tasks/deleteitemtask.cpp new file mode 100644 index 00000000..89480d10 --- /dev/null +++ b/kopete/protocols/groupwise/libgroupwise/tasks/deleteitemtask.cpp @@ -0,0 +1,46 @@ +/* + Kopete Groupwise Protocol + deleteitemtask.cpp - Delete a contact or folder on the server + + Copyright (c) 2004 SUSE Linux AG http://www.suse.com + + Based on Iris, Copyright (C) 2003 Justin Karneges + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#include "deleteitemtask.h" + +DeleteItemTask::DeleteItemTask(Task* parent): ModifyContactListTask(parent) +{ +} + + +DeleteItemTask::~DeleteItemTask() +{ +} + +void DeleteItemTask::item( const int parentFolder, const int objectId ) +{ + if ( objectId == 0 ) + { + setError( 1, "Can't delete the root folder" ); + return; + } + Field::FieldList lst; + lst.append( new Field::SingleField( NM_A_SZ_PARENT_ID, 0, NMFIELD_TYPE_UTF8, QString::number( parentFolder ) ) ); + // this is either a user Id or a DN + lst.append( new Field::SingleField( NM_A_SZ_OBJECT_ID, 0, NMFIELD_TYPE_UTF8, QString::number( objectId ) ) ); + createTransfer( "deletecontact", lst ); +} + +#include "deleteitemtask.moc" diff --git a/kopete/protocols/groupwise/libgroupwise/tasks/deleteitemtask.h b/kopete/protocols/groupwise/libgroupwise/tasks/deleteitemtask.h new file mode 100644 index 00000000..f249c2f5 --- /dev/null +++ b/kopete/protocols/groupwise/libgroupwise/tasks/deleteitemtask.h @@ -0,0 +1,38 @@ +/* + Kopete Groupwise Protocol + deleteitemtask.h - Delete a contact or folder on the server + + Copyright (c) 2004 SUSE Linux AG http://www.suse.com + + Based on Iris, Copyright (C) 2003 Justin Karneges + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#ifndef DELETEITEMTASK_H +#define DELETEITEMTASK_H + +#include "modifycontactlisttask.h" + +/** +@author SUSE AG +*/ +class DeleteItemTask : public ModifyContactListTask +{ +Q_OBJECT +public: + DeleteItemTask(Task* parent); + ~DeleteItemTask(); + void item( const int parentFolder, const int objectId ); +}; + +#endif diff --git a/kopete/protocols/groupwise/libgroupwise/tasks/eventtask.cpp b/kopete/protocols/groupwise/libgroupwise/tasks/eventtask.cpp new file mode 100644 index 00000000..c6bd2d85 --- /dev/null +++ b/kopete/protocols/groupwise/libgroupwise/tasks/eventtask.cpp @@ -0,0 +1,48 @@ +/* + Kopete Groupwise Protocol + eventtask.cpp - Ancestor of all Event Handling tasks + + Copyright (c) 2004 SUSE Linux AG http://www.suse.com + + Based on Iris, Copyright (C) 2003 Justin Karneges + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#include "gwfield.h" +#include "eventtask.h" + +EventTask::EventTask( Task * parent ) +: Task( parent ) +{ +} + +void EventTask::registerEvent( GroupWise::Event e ) +{ + m_eventCodes.append( e ); +} + +bool EventTask::forMe( Transfer * transfer, EventTransfer*& event ) const +{ + // see if we can down-cast transfer to an EventTransfer + /*EventTransfer * */ + event = dynamic_cast<EventTransfer *>(transfer); + if ( event ) + { + // see if we are supposed to handle this kind of event + // consider speeding this up by having 1 handler per event + return ( m_eventCodes.find( event->eventType() ) != m_eventCodes.end() ); + } + return false; +} + +#include "eventtask.moc" diff --git a/kopete/protocols/groupwise/libgroupwise/tasks/eventtask.h b/kopete/protocols/groupwise/libgroupwise/tasks/eventtask.h new file mode 100644 index 00000000..50b84ac5 --- /dev/null +++ b/kopete/protocols/groupwise/libgroupwise/tasks/eventtask.h @@ -0,0 +1,43 @@ +/* + Kopete Groupwise Protocol + eventtask.h - Ancestor of all Event Handling tasks + + Copyright (c) 2004 SUSE Linux AG http://www.suse.com + + Based on Iris, Copyright (C) 2003 Justin Karneges + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#ifndef GW_EVENTTASK_H +#define GW_EVENTTASK_H + +#include <qvaluelist.h> + +#include "eventtransfer.h" +#include "task.h" + +class Transfer; + +class EventTask : public Task +{ +Q_OBJECT + public: + EventTask( Task *parent ); + protected: + bool forMe( Transfer * transfer, EventTransfer *& event ) const; + void registerEvent( GroupWise::Event e ); + private: + QValueList<int> m_eventCodes; +}; + +#endif diff --git a/kopete/protocols/groupwise/libgroupwise/tasks/getchatsearchresultstask.cpp b/kopete/protocols/groupwise/libgroupwise/tasks/getchatsearchresultstask.cpp new file mode 100644 index 00000000..fe1d61f9 --- /dev/null +++ b/kopete/protocols/groupwise/libgroupwise/tasks/getchatsearchresultstask.cpp @@ -0,0 +1,122 @@ +/* + Kopete Groupwise Protocol + getchatsearchresultstask.cpp - Poll the server to see if it has processed our search yet. + + Copyright (c) 2004 SUSE Linux AG http://www.suse.com + + Based on Iris, Copyright (C) 2003 Justin Karneges + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#include <kdebug.h> + +#include "gwfield.h" +#include "response.h" + +#include "logintask.h" + +#include "getchatsearchresultstask.h" + +using namespace GroupWise; + +GetChatSearchResultsTask::GetChatSearchResultsTask(Task* parent): RequestTask(parent) +{ +} + + +GetChatSearchResultsTask::~GetChatSearchResultsTask() +{ +} + +void GetChatSearchResultsTask::poll( int queryHandle ) +{ + Field::FieldList lst; + lst.append( new Field::SingleField( NM_A_UD_OBJECT_ID, 0, NMFIELD_TYPE_UDWORD, queryHandle ) ); + lst.append( new Field::SingleField( NM_A_UD_QUERY_COUNT, 0, NMFIELD_TYPE_UDWORD, 10 ) ); + createTransfer( "getchatsearchresults", lst ); +} + +bool GetChatSearchResultsTask::take( Transfer * transfer ) +{ + if ( !forMe( transfer ) ) + return false; + Response * response = dynamic_cast<Response *>( transfer ); + if ( !response ) + return false; + if ( response->resultCode() ) + { + setError( response->resultCode() ); + return true; + } + + // look for the status code + Field::FieldList responseFields = response->fields(); + Field::SingleField * sf = responseFields.findSingleField( NM_A_UW_STATUS ); + m_queryStatus = (SearchResultCode)sf->value().toInt(); + + Field::MultiField * resultsArray = responseFields.findMultiField( NM_A_FA_RESULTS ); + if ( !resultsArray ) + { + setError( Protocol ); + return true; + } + Field::FieldList matches = resultsArray->fields(); + const Field::FieldListIterator end = matches.end(); + for ( Field::FieldListIterator it = matches.find( NM_A_FA_CHAT ); + it != end; + it = matches.find( ++it, NM_A_FA_CHAT ) ) + { + Field::MultiField * mf = static_cast<Field::MultiField *>( *it ); + Field::FieldList chat = mf->fields(); + GroupWise::ChatroomSearchResult cd = extractChatDetails( chat ); + m_results.append( cd ); + } + + if ( m_queryStatus != DataRetrieved ) + setError( m_queryStatus ); + else + { + kdDebug ( GROUPWISE_DEBUG_GLOBAL ) << " we won!" << endl; + setSuccess( m_queryStatus ); + } + return true; +} + +QValueList< GroupWise::ChatroomSearchResult > GetChatSearchResultsTask::results() +{ + return m_results; +} + +int GetChatSearchResultsTask::queryStatus() +{ + return m_queryStatus; +} + +GroupWise::ChatroomSearchResult GetChatSearchResultsTask::extractChatDetails( Field::FieldList & fields ) +{ + ChatroomSearchResult csr; + csr.participants = 0; + // read the supplied fields, set metadata and status. + Field::SingleField * sf; + if ( ( sf = fields.findSingleField ( NM_A_DISPLAY_NAME ) ) ) + csr.name = sf->value().toString(); + if ( ( sf = fields.findSingleField ( NM_A_CHAT_OWNER_DN ) ) ) + csr.ownerDN = sf->value().toString().lower(); // HACK: lowercased DN + if ( ( sf = fields.findSingleField ( NM_A_UD_PARTICIPANTS ) ) ) + csr.participants = sf->value().toInt(); + + kdDebug( GROUPWISE_DEBUG_GLOBAL ) << csr.name << ", " << csr.ownerDN << ", " << csr.participants << endl; + return csr; +} + +#include "getchatsearchresultstask.moc" diff --git a/kopete/protocols/groupwise/libgroupwise/tasks/getchatsearchresultstask.h b/kopete/protocols/groupwise/libgroupwise/tasks/getchatsearchresultstask.h new file mode 100644 index 00000000..31db19ed --- /dev/null +++ b/kopete/protocols/groupwise/libgroupwise/tasks/getchatsearchresultstask.h @@ -0,0 +1,52 @@ +/* + Kopete Groupwise Protocol + getchatsearchresultstask.h - Poll the server once to see if it has processed our chatroom search yet. + + Copyright (c) 2005 SUSE Linux Products GmbH http://www.suse.com + + Based on Iris, Copyright (C) 2003 Justin Karneges + + Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#ifndef CHATSEARCHRESULTSTASK_H +#define CHATSEARCHRESULTSTASK_H + +#include <qvaluelist.h> + +#include "gwchatrooms.h" + +#include "requesttask.h" + +/** +Search results are polled on the server, using the search handle returned by the server with the original query. This is a single poll request, which if successful, will retrieve the results. Otherwise, it will set a status code, so the SearchChatTask can decide whether to poll again. + +@author SUSE Linux Products GmbH + */ +class GetChatSearchResultsTask : public RequestTask +{ + Q_OBJECT + public: + enum SearchResultCode { Completed=2, Cancelled=4, Error=5, GettingData=8, DataRetrieved=9 }; + GetChatSearchResultsTask(Task* parent); + ~GetChatSearchResultsTask(); + void poll( int queryHandle); + bool take( Transfer * transfer ); + int queryStatus(); + QValueList< GroupWise::ChatroomSearchResult > results(); + private: + GroupWise::ChatroomSearchResult extractChatDetails( Field::FieldList & fields ); + SearchResultCode m_queryStatus; + QValueList< GroupWise::ChatroomSearchResult > m_results; +}; + +#endif diff --git a/kopete/protocols/groupwise/libgroupwise/tasks/getdetailstask.cpp b/kopete/protocols/groupwise/libgroupwise/tasks/getdetailstask.cpp new file mode 100644 index 00000000..0b37efb4 --- /dev/null +++ b/kopete/protocols/groupwise/libgroupwise/tasks/getdetailstask.cpp @@ -0,0 +1,136 @@ +/* + Kopete Groupwise Protocol + getdetailstask.cpp - fetch a contact's details from the server + + Copyright (c) 2004 SUSE Linux AG http://www.suse.com + + Based on Iris, Copyright (C) 2003 Justin Karneges + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#include "client.h" +#include "response.h" +#include "userdetailsmanager.h" + +#include "getdetailstask.h" + +using namespace GroupWise; + +GetDetailsTask::GetDetailsTask( Task * parent ) + : RequestTask( parent ) +{ +} + + +GetDetailsTask::~GetDetailsTask() +{ +} + +void GetDetailsTask::userDNs( const QStringList & userDNs ) +{ + Field::FieldList lst; + for ( QStringList::ConstIterator it = userDNs.begin(); it != userDNs.end(); ++it ) + { + lst.append( new Field::SingleField( NM_A_SZ_DN, 0, NMFIELD_TYPE_UTF8, *it ) ); + } + createTransfer( "getdetails", lst ); +} + +bool GetDetailsTask::take( Transfer * transfer ) +{ + if ( !forMe( transfer ) ) + return false; + Response * response = dynamic_cast<Response *>( transfer ); + if ( !response ) + return false; + + Field::FieldList detailsFields = response->fields(); + // parse received details and signal like billio + Field::MultiField * container = 0; + Field::FieldListIterator end = detailsFields.end(); + for ( Field::FieldListIterator it = detailsFields.find( NM_A_FA_RESULTS ); + it != end; + it = detailsFields.find( ++it, NM_A_FA_RESULTS ) ) + { + container = static_cast<Field::MultiField *>( *it ); + ContactDetails cd = extractUserDetails( container ); + emit gotContactUserDetails( cd ); + } + + return true; +} + +ContactDetails GetDetailsTask::extractUserDetails(Field::MultiField * details ) +{ + ContactDetails cd; + cd.status = GroupWise::Invalid; + cd.archive = false; + Field::FieldList fields = details->fields(); + // TODO: not sure what this means, ask Mike + Field::SingleField * sf; + if ( ( sf = fields.findSingleField ( NM_A_SZ_AUTH_ATTRIBUTE ) ) ) + cd.authAttribute = sf->value().toString(); + if ( ( sf = fields.findSingleField ( NM_A_SZ_DN ) ) ) + cd.dn =sf->value().toString().lower(); // HACK: lowercased DN + if ( ( sf = fields.findSingleField ( "CN" ) ) ) + cd.cn = sf->value().toString(); + if ( ( sf = fields.findSingleField ( "Given Name" ) ) ) + cd.givenName = sf->value().toString(); + if ( ( sf = fields.findSingleField ( "Surname" ) ) ) + cd.surname = sf->value().toString(); + if ( ( sf = fields.findSingleField ( "nnmArchive" ) ) ) + cd.archive = ( sf->value().toInt() == 1 ); + if ( ( sf = fields.findSingleField ( "Full Name" ) ) ) + cd.fullName = sf->value().toString(); + if ( ( sf = fields.findSingleField ( NM_A_SZ_STATUS ) ) ) + cd.status = sf->value().toInt(); + if ( ( sf = fields.findSingleField ( NM_A_SZ_MESSAGE_BODY ) ) ) + cd.awayMessage = sf->value().toString(); + Field::MultiField * mf; + QMap< QString, QString > propMap; + if ( ( mf = fields.findMultiField ( NM_A_FA_INFO_DISPLAY_ARRAY ) ) ) + { + Field::FieldList fl = mf->fields(); + const Field::FieldListIterator end = fl.end(); + for ( Field::FieldListIterator it = fl.begin(); it != end; ++it ) + { + Field::SingleField * propField = dynamic_cast<Field::SingleField *>( *it ); + if ( propField ) { + QString propName = propField->tag(); + QString propValue = propField->value().toString(); + propMap.insert( propName, propValue ); + } else { + Field::MultiField * mf2; + if ( ( mf2 = dynamic_cast<Field::MultiField *>( *it ) ) ) { + Field::FieldList fl2 = mf2->fields(); + const Field::FieldListIterator end = fl2.end(); + for ( Field::FieldListIterator it2 = fl2.begin(); it2 != end; ++it2 ) + { + propField = dynamic_cast<Field::SingleField *>( *it2 ); + if ( propField ) { + QString propName = propField->tag(); + QString propValue = propField->value().toString(); + propMap.insert( propName, propValue ); + } + } + } + } + } + } + if ( !propMap.empty() ) + { + cd.properties = propMap; + } + return cd; +} +#include "getdetailstask.moc" diff --git a/kopete/protocols/groupwise/libgroupwise/tasks/getdetailstask.h b/kopete/protocols/groupwise/libgroupwise/tasks/getdetailstask.h new file mode 100644 index 00000000..d263f50b --- /dev/null +++ b/kopete/protocols/groupwise/libgroupwise/tasks/getdetailstask.h @@ -0,0 +1,49 @@ +/* + Kopete Groupwise Protocol + getdetailstask.h - fetch a contact's details from the server + + Copyright (c) 2004 SUSE Linux AG http://www.suse.com + + Based on Iris, Copyright (C) 2003 Justin Karneges + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#ifndef GETDETAILSTASK_H +#define GETDETAILSTASK_H + +#include "gwerror.h" +#include "requesttask.h" + +/** +This task fetches the details for a set of user IDs from the server. Sometimes we get an event that only has a DN, and we need other details before showing the event to the user. + +@author SUSE AG +*/ +using namespace GroupWise; + +class GetDetailsTask : public RequestTask +{ +Q_OBJECT +public: + GetDetailsTask( Task * parent ); + ~GetDetailsTask(); + bool take( Transfer * transfer ); + void userDNs( const QStringList & userDNs ); +signals: + void gotContactUserDetails( const GroupWise::ContactDetails & ); +protected: + GroupWise::ContactDetails extractUserDetails( Field::MultiField * details ); + +}; + +#endif diff --git a/kopete/protocols/groupwise/libgroupwise/tasks/getstatustask.cpp b/kopete/protocols/groupwise/libgroupwise/tasks/getstatustask.cpp new file mode 100644 index 00000000..dde055a6 --- /dev/null +++ b/kopete/protocols/groupwise/libgroupwise/tasks/getstatustask.cpp @@ -0,0 +1,72 @@ +/* + Kopete Groupwise Protocol + getstatustask.cpp - fetch a contact's details from the server + + Copyright (c) 2004 SUSE Linux AG http://www.suse.com + + Based on Iris, Copyright (C) 2003 Justin Karneges + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#include "response.h" + +#include "getstatustask.h" + +GetStatusTask::GetStatusTask(Task* parent): RequestTask(parent) +{ +} + +GetStatusTask::~GetStatusTask() +{ +} + +void GetStatusTask::userDN( const QString & dn ) +{ + m_userDN = dn; + // set up Transfer + Field::FieldList lst; + // changed from USERID to DN as per Gaim/GWIM + lst.append( new Field::SingleField( NM_A_SZ_DN, 0, NMFIELD_TYPE_UTF8, m_userDN ) ); + createTransfer( "getstatus", lst ); +} + +bool GetStatusTask::take( Transfer * transfer ) +{ + if ( !forMe( transfer ) ) + return false; + Response * response = dynamic_cast<Response *>( transfer ); + if ( !response ) + return false; + + Field::FieldList responseFields = response->fields(); + responseFields.dump( true ); + // parse received details and signal like billio + Field::SingleField * sf = 0; + Q_UINT16 status; + sf = responseFields.findSingleField( NM_A_SZ_STATUS ); + if ( sf ) + { + // As of Sept 2004 the server always responds with 2 (Available) here, even if the sender is not + // This must be because the sender is not on our contact list but has sent us a message. + // TODO: Check that the change to sending DNs above has fixed this problem. + status = sf->value().toInt(); + // unfortunately getstatus doesn't give us an away message so we pass QString::null here + emit gotStatus( m_userDN, status, QString::null ); + setSuccess(); + } + else + setError(); + return true; +} + +#include "getstatustask.moc" diff --git a/kopete/protocols/groupwise/libgroupwise/tasks/getstatustask.h b/kopete/protocols/groupwise/libgroupwise/tasks/getstatustask.h new file mode 100644 index 00000000..59422342 --- /dev/null +++ b/kopete/protocols/groupwise/libgroupwise/tasks/getstatustask.h @@ -0,0 +1,44 @@ +/* + Kopete Groupwise Protocol + getstatustask.h - fetch a contact's details from the server + + Copyright (c) 2004 SUSE Linux AG http://www.suse.com + + Based on Iris, Copyright (C) 2003 Justin Karneges + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#ifndef GETSTATUSTASK_H +#define GETSTATUSTASK_H + +#include "requesttask.h" + +/** + * Request the status for a specific contact (e.g. one who's not on our contact list) + * @author SUSE AG +*/ +class GetStatusTask : public RequestTask +{ +Q_OBJECT +public: + GetStatusTask(Task* parent); + ~GetStatusTask(); + void userDN( const QString & dn ); + bool take( Transfer * transfer ); +signals: + void gotStatus( const QString & contactId, Q_UINT16 status, const QString & statusText ); +private: + QString m_userDN; +}; + +#endif diff --git a/kopete/protocols/groupwise/libgroupwise/tasks/gwtasklogin.cpp b/kopete/protocols/groupwise/libgroupwise/tasks/gwtasklogin.cpp new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/kopete/protocols/groupwise/libgroupwise/tasks/gwtasklogin.cpp diff --git a/kopete/protocols/groupwise/libgroupwise/tasks/gwtasklogin.h b/kopete/protocols/groupwise/libgroupwise/tasks/gwtasklogin.h new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/kopete/protocols/groupwise/libgroupwise/tasks/gwtasklogin.h diff --git a/kopete/protocols/groupwise/libgroupwise/tasks/joinchattask.cpp b/kopete/protocols/groupwise/libgroupwise/tasks/joinchattask.cpp new file mode 100644 index 00000000..4e9e4f57 --- /dev/null +++ b/kopete/protocols/groupwise/libgroupwise/tasks/joinchattask.cpp @@ -0,0 +1,131 @@ +/* + Kopete Groupwise Protocol + joinchattask.cpp - Join a Chat on the server, after having been invited. + + Copyright (c) 2005 SUSE Linux Products GmbH http://www.suse.com + + Based on Iris, Copyright (C) 2003 Justin Karneges + + Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#include "gwerror.h" +#include "client.h" +#include "response.h" +#include "userdetailsmanager.h" + +#include "joinchattask.h" + +JoinChatTask::JoinChatTask(Task* parent): RequestTask(parent) +{ +} + +JoinChatTask::~JoinChatTask() +{ +} + +void JoinChatTask::join( const QString & displayName ) +{ + m_displayName = displayName; + Field::FieldList lst, tmp; + tmp.append( new Field::SingleField( NM_A_SZ_OBJECT_ID, 0, NMFIELD_TYPE_UTF8, displayName ) ); + lst.append( new Field::MultiField( NM_A_FA_CONVERSATION, NMFIELD_METHOD_VALID, 0, NMFIELD_TYPE_ARRAY, tmp ) ); + createTransfer( "joinchat", lst ); +} + +bool JoinChatTask::take( Transfer * transfer ) +{ + if ( forMe( transfer ) ) + { + client()->debug( "JoinChatTask::take()" ); + Response * response = dynamic_cast<Response *>( transfer ); + Field::FieldList responseFields = response->fields(); + // if the request was successful + if ( response->resultCode() == GroupWise::None ) + { + // extract the list of participants and store them + Field::MultiField * participants = responseFields.findMultiField( NM_A_FA_CONTACT_LIST ); + if ( participants ) + { + Field::SingleField * contact = 0; + Field::FieldList contactList = participants->fields(); + const Field::FieldListIterator end = contactList.end(); + for ( Field::FieldListIterator it = contactList.find( NM_A_SZ_DN ); + it != end; + it = contactList.find( ++it, NM_A_SZ_DN ) ) + { + contact = static_cast<Field::SingleField *>( *it ); + if ( contact ) + { + // HACK: lowercased DN + QString dn = contact->value().toString().lower(); + m_participants.append( dn ); + // need to ask for details for these contacts + } + } + } + else + setError( GroupWise::Protocol ); + + // now, extract the list of pending invites and store them + Field::MultiField * invitees = responseFields.findMultiField( NM_A_FA_RESULTS ); + if ( invitees ) + { + Field::SingleField * contact = 0; + Field::FieldList contactList = invitees->fields(); + const Field::FieldListIterator end = contactList.end(); + for ( Field::FieldListIterator it = contactList.find( NM_A_SZ_DN ); + it != end; + it = contactList.find( ++it, NM_A_SZ_DN ) ) + { + contact = static_cast<Field::SingleField *>( *it ); + if ( contact ) + { + // HACK: lowercased DN + QString dn = contact->value().toString().lower(); + m_invitees.append( dn ); + // need to ask for details for these contacts + if ( !client()->userDetailsManager()->known( dn ) ) + ; // don't request details for chatrooms, there could be too many + } + } + } + else + setError( GroupWise::Protocol ); + + client()->debug( "JoinChatTask::finished()" ); + finished(); + } + else + setError( response->resultCode() ); + return true; + } + else + return false; +} + +QStringList JoinChatTask::participants() const +{ + return m_participants; +} + +QStringList JoinChatTask::invitees() const +{ + return m_invitees; +} + +QString JoinChatTask::displayName() const +{ + return m_displayName; +} + +#include "joinchattask.moc" diff --git a/kopete/protocols/groupwise/libgroupwise/tasks/joinchattask.h b/kopete/protocols/groupwise/libgroupwise/tasks/joinchattask.h new file mode 100644 index 00000000..a7cc4119 --- /dev/null +++ b/kopete/protocols/groupwise/libgroupwise/tasks/joinchattask.h @@ -0,0 +1,52 @@ +/* + Kopete Groupwise Protocol + joinchattask.h - Join a chatroom on the server, after having been invited. + + Copyright (c) 2004 SUSE Linux Products GmbH http://www.suse.com + + Based on Iris, Copyright (C) 2003 Justin Karneges + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#ifndef JOINCHATTASK_H +#define JOINCHATTASK_H + +#include "requesttask.h" + +using namespace GroupWise; + +/** +Sends Join Conference messages when the user accepts an invitation + +@author SUSE Linux Products GmbH + */ + +class JoinChatTask : public RequestTask +{ + Q_OBJECT + public: + JoinChatTask(Task* parent); + ~JoinChatTask(); + void join( const QString & displayName ); + bool take( Transfer * transfer ); + QStringList participants() const; + QStringList invitees() const; + QString displayName() const; + private: + ConferenceGuid m_displayName; + QStringList m_participants; + QStringList m_invitees; + QStringList m_unknowns; +}; + +#endif diff --git a/kopete/protocols/groupwise/libgroupwise/tasks/joinconferencetask.cpp b/kopete/protocols/groupwise/libgroupwise/tasks/joinconferencetask.cpp new file mode 100644 index 00000000..c2cf0f02 --- /dev/null +++ b/kopete/protocols/groupwise/libgroupwise/tasks/joinconferencetask.cpp @@ -0,0 +1,175 @@ +/* + Kopete Groupwise Protocol + joinconferencetask.cpp - Join a conference on the server, after having been invited. + + Copyright (c) 2004 SUSE Linux AG http://www.suse.com + + Based on Iris, Copyright (C) 2003 Justin Karneges + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#include "gwerror.h" +#include "client.h" +#include "response.h" +#include "userdetailsmanager.h" + +#include "joinconferencetask.h" + +JoinConferenceTask::JoinConferenceTask(Task* parent): RequestTask(parent) +{ +} + +JoinConferenceTask::~JoinConferenceTask() +{ +} + +void JoinConferenceTask::join( const GroupWise::ConferenceGuid & guid ) +{ + m_guid = guid; + Field::FieldList lst, tmp; + tmp.append( new Field::SingleField( NM_A_SZ_OBJECT_ID, 0, NMFIELD_TYPE_UTF8, guid ) ); + lst.append( new Field::MultiField( NM_A_FA_CONVERSATION, NMFIELD_METHOD_VALID, 0, NMFIELD_TYPE_ARRAY, tmp ) ); + createTransfer( "joinconf", lst ); +} + +bool JoinConferenceTask::take( Transfer * transfer ) +{ + if ( forMe( transfer ) ) + { + client()->debug( "JoinConferenceTask::take()" ); + Response * response = dynamic_cast<Response *>( transfer ); + Field::FieldList responseFields = response->fields(); + // if the request was successful + if ( response->resultCode() == GroupWise::None ) + { + // extract the list of participants and store them + Field::MultiField * participants = responseFields.findMultiField( NM_A_FA_CONTACT_LIST ); + if ( participants ) + { + Field::SingleField * contact = 0; + Field::FieldList contactList = participants->fields(); + const Field::FieldListIterator end = contactList.end(); + for ( Field::FieldListIterator it = contactList.find( NM_A_SZ_DN ); + it != end; + it = contactList.find( ++it, NM_A_SZ_DN ) ) + { + contact = static_cast<Field::SingleField *>( *it ); + if ( contact ) + { + // HACK: lowercased DN + QString dn = contact->value().toString().lower(); + m_participants.append( dn ); + // need to ask for details for these contacts + if ( !client()->userDetailsManager()->known( dn ) ) + m_unknowns.append( dn ); + } + } + } + else + setError( GroupWise::Protocol ); + + // now, extract the list of pending invites and store them + Field::MultiField * invitees = responseFields.findMultiField( NM_A_FA_RESULTS ); + if ( invitees ) + { + Field::SingleField * contact = 0; + Field::FieldList contactList = invitees->fields(); + const Field::FieldListIterator end = contactList.end(); + for ( Field::FieldListIterator it = contactList.find( NM_A_SZ_DN ); + it != end; + it = contactList.find( ++it, NM_A_SZ_DN ) ) + { + contact = static_cast<Field::SingleField *>( *it ); + if ( contact ) + { + // HACK: lowercased DN + QString dn = contact->value().toString().lower(); + m_invitees.append( dn ); + // need to ask for details for these contacts + if ( !client()->userDetailsManager()->known( dn ) ) + m_unknowns.append( dn ); + } + } + } + else + setError( GroupWise::Protocol ); + + if ( m_unknowns.empty() ) // ready to chat + { + client()->debug( "JoinConferenceTask::finished()" ); + finished(); + } + else // need to get some more details first + { + client()->debug( "JoinConferenceTask::slotReceiveUserDetails(), requesting details" ); + connect( client()->userDetailsManager(), + SIGNAL( gotContactDetails( const GroupWise::ContactDetails & ) ), + SLOT( slotReceiveUserDetails( const GroupWise::ContactDetails & ) ) ); + client()->userDetailsManager()->requestDetails( m_unknowns ); + } + } + else + setError( response->resultCode() ); + return true; + } + else + return false; +} + +void JoinConferenceTask::slotReceiveUserDetails( const ContactDetails & details ) +{ + client()->debug( QString( "JoinConferenceTask::slotReceiveUserDetails() - got %1" ).arg( details.dn ) ); + QStringList::Iterator it = m_unknowns.begin(); + QStringList::Iterator end = m_unknowns.end(); + while( it != end ) + { + QString current = *it; + ++it; + client()->debug( QString( " - can we remove %1?" ).arg(current ) ); + if ( current == details.dn ) + { + client()->debug( " - it's gone!" ); + m_unknowns.remove( current ); + break; + } + } + client()->debug( QString( " - now %1 unknowns").arg( m_unknowns.count() ) ); + if ( m_unknowns.empty() ) + { + client()->debug( " - finished()" ); + finished(); + } +// would be better to count the number of received details and listen to the getdetails task's error signal. +// else +// { +// client()->debug( " - ERROR - we requested details for the list of chat participants/invitees, but the server did not send us all the details! - setting finished() anyway, so the chat can take place." ); +// finished(); +// } +} + +QStringList JoinConferenceTask::participants() const +{ + return m_participants; +} + +QStringList JoinConferenceTask::invitees() const +{ + return m_invitees; +} + +GroupWise::ConferenceGuid JoinConferenceTask::guid() const +{ + return m_guid; +} + +#include "joinconferencetask.moc" diff --git a/kopete/protocols/groupwise/libgroupwise/tasks/joinconferencetask.h b/kopete/protocols/groupwise/libgroupwise/tasks/joinconferencetask.h new file mode 100644 index 00000000..68316147 --- /dev/null +++ b/kopete/protocols/groupwise/libgroupwise/tasks/joinconferencetask.h @@ -0,0 +1,54 @@ +/* + Kopete Groupwise Protocol + joinconferencetask.h - Join a conference on the server, after having been invited. + + Copyright (c) 2004 SUSE Linux AG http://www.suse.com + + Based on Iris, Copyright (C) 2003 Justin Karneges + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#ifndef JOINCONFERENCETASK_H +#define JOINCONFERENCETASK_H + +#include "requesttask.h" + +using namespace GroupWise; + +/** +Sends Join Conference messages when the user accepts an invitation + +@author SUSE AG +*/ + +class JoinConferenceTask : public RequestTask +{ +Q_OBJECT +public: + JoinConferenceTask(Task* parent); + ~JoinConferenceTask(); + void join( const ConferenceGuid & guid ); + bool take( Transfer * transfer ); + QStringList participants() const; + QStringList invitees() const; + ConferenceGuid guid() const; +public slots: + void slotReceiveUserDetails( const GroupWise::ContactDetails & details ); +private: + ConferenceGuid m_guid; + QStringList m_participants; + QStringList m_invitees; + QStringList m_unknowns; +}; + +#endif diff --git a/kopete/protocols/groupwise/libgroupwise/tasks/keepalivetask.cpp b/kopete/protocols/groupwise/libgroupwise/tasks/keepalivetask.cpp new file mode 100644 index 00000000..ac84ac2b --- /dev/null +++ b/kopete/protocols/groupwise/libgroupwise/tasks/keepalivetask.cpp @@ -0,0 +1,42 @@ +/* + Kopete Groupwise Protocol + keepalivetask.cpp - Send keepalive pings to the server + + Copyright (c) 2004 SUSE Linux AG http://www.suse.com + (c) 2006 Novell, Inc. + + Based on Iris, Copyright (C) 2003 Justin Karneges + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#include "client.h" +#include "request.h" +#include "requestfactory.h" +#include "keepalivetask.h" + +KeepAliveTask::KeepAliveTask(Task* parent): RequestTask(parent) +{ +} + + +KeepAliveTask::~KeepAliveTask() +{ +} + +void KeepAliveTask::setup() +{ + Field::FieldList lst; + createTransfer( "ping", lst ); +} + +#include "keepalivetask.moc" diff --git a/kopete/protocols/groupwise/libgroupwise/tasks/keepalivetask.h b/kopete/protocols/groupwise/libgroupwise/tasks/keepalivetask.h new file mode 100644 index 00000000..04f9a352 --- /dev/null +++ b/kopete/protocols/groupwise/libgroupwise/tasks/keepalivetask.h @@ -0,0 +1,38 @@ +/* + Kopete Groupwise Protocol + keepalivetask.h - Send keepalive pings to the server + + Copyright (c) 2004 SUSE Linux AG http://www.suse.com + + Based on Iris, Copyright (C) 2003 Justin Karneges + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#ifndef KEEPALIVETASK_H +#define KEEPALIVETASK_H + +#include "requesttask.h" + +/** +@author Kopete Developers +*/ +class KeepAliveTask : public RequestTask +{ +Q_OBJECT +public: + KeepAliveTask(Task* parent); + ~KeepAliveTask(); + void setup(); +}; + +#endif diff --git a/kopete/protocols/groupwise/libgroupwise/tasks/leaveconferencetask.cpp b/kopete/protocols/groupwise/libgroupwise/tasks/leaveconferencetask.cpp new file mode 100644 index 00000000..d2d58b83 --- /dev/null +++ b/kopete/protocols/groupwise/libgroupwise/tasks/leaveconferencetask.cpp @@ -0,0 +1,40 @@ +/* + Kopete Groupwise Protocol + leaveconferencetask.cpp - Tell the server we are leaving a conference + + Copyright (c) 2004 SUSE Linux AG http://www.suse.com + + Based on Iris, Copyright (C) 2003 Justin Karneges + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#include "leaveconferencetask.h" + +LeaveConferenceTask::LeaveConferenceTask(Task* parent): RequestTask(parent) +{ +} + + +LeaveConferenceTask::~LeaveConferenceTask() +{ +} + +void LeaveConferenceTask::leave( const GroupWise::ConferenceGuid & guid ) +{ + Field::FieldList lst, tmp; + tmp.append( new Field::SingleField( NM_A_SZ_OBJECT_ID, 0, NMFIELD_TYPE_UTF8, guid ) ); + lst.append( new Field::MultiField( NM_A_FA_CONVERSATION, NMFIELD_METHOD_VALID, 0, NMFIELD_TYPE_ARRAY, tmp ) ); + createTransfer( "leaveconf", lst ); +} + +#include "leaveconferencetask.moc" diff --git a/kopete/protocols/groupwise/libgroupwise/tasks/leaveconferencetask.h b/kopete/protocols/groupwise/libgroupwise/tasks/leaveconferencetask.h new file mode 100644 index 00000000..65ebe540 --- /dev/null +++ b/kopete/protocols/groupwise/libgroupwise/tasks/leaveconferencetask.h @@ -0,0 +1,40 @@ +/* + Kopete Groupwise Protocol + leaveconferencetask.h - Tell the server we are leaving a conference + + Copyright (c) 2004 SUSE Linux AG http://www.suse.com + + Based on Iris, Copyright (C) 2003 Justin Karneges + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#ifndef LEAVECONFERENCETASK_H +#define LEAVECONFERENCETASK_H + +#include "requesttask.h" + +/** +Tells the server that you are leaving a conference (closed the chatwindow) + +@author SUSE AG +*/ +class LeaveConferenceTask : public RequestTask +{ +Q_OBJECT +public: + LeaveConferenceTask(Task* parent); + ~LeaveConferenceTask(); + void leave( const GroupWise::ConferenceGuid & guid ); +}; + +#endif diff --git a/kopete/protocols/groupwise/libgroupwise/tasks/logintask.cpp b/kopete/protocols/groupwise/libgroupwise/tasks/logintask.cpp new file mode 100644 index 00000000..1f679a6c --- /dev/null +++ b/kopete/protocols/groupwise/libgroupwise/tasks/logintask.cpp @@ -0,0 +1,360 @@ +/* + Kopete Groupwise Protocol + logintask.cpp - Send our credentials to the server and process the contact list and privacy details that it returns. + + Copyright (c) 2004 SUSE Linux AG http://www.suse.com + + Based on Iris, Copyright (C) 2003 Justin Karneges + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#include "client.h" +#include "response.h" +#include "privacymanager.h" +#include "userdetailsmanager.h" + +#include "logintask.h" + +LoginTask::LoginTask( Task * parent ) + : RequestTask( parent ) +{ +} + +LoginTask::~LoginTask() +{ +} + +void LoginTask::initialise() +{ + QString command = QString::fromLatin1("login:%1:%2").arg( client()->host() ).arg( client()->port() ); + + Field::FieldList lst; + lst.append( new Field::SingleField( NM_A_SZ_USERID, 0, NMFIELD_TYPE_UTF8, client()->userId() ) ); + lst.append( new Field::SingleField( NM_A_SZ_CREDENTIALS, 0, NMFIELD_TYPE_UTF8, client()->password() ) ); + lst.append( new Field::SingleField( NM_A_SZ_USER_AGENT, 0, NMFIELD_TYPE_UTF8, client()->userAgent() ) ); + lst.append( new Field::SingleField( NM_A_UD_BUILD, 0, NMFIELD_TYPE_UDWORD, client()->protocolVersion() ) ); + lst.append( new Field::SingleField( NM_A_IP_ADDRESS, 0, NMFIELD_TYPE_UTF8, client()->ipAddress() ) ); + createTransfer( command, lst ); +} + +bool LoginTask::take( Transfer * transfer ) +{ + if ( !forMe( transfer ) ) + return false; + Response * response = dynamic_cast<Response *>( transfer ); + if ( !response ) + return false; + if ( response->resultCode() ) + { + setError( response->resultCode() ); + return true; + } + response->fields().dump( true ); + + // read in myself()'s metadata fields and emit signal + Field::FieldList loginResponseFields = response->fields(); + + ContactDetails cd = extractUserDetails( loginResponseFields ); + emit gotMyself( cd ); + + // read the privacy settings first, because this affects all contacts' apparent status + extractPrivacy( loginResponseFields ); + + extractCustomStatuses( loginResponseFields ); + + // CREATE CONTACT LIST + // locate contact list + Field::MultiField * contactList = loginResponseFields.findMultiField( NM_A_FA_CONTACT_LIST ); + if ( contactList ) + { + Field::FieldList contactListFields = contactList->fields(); + Field::MultiField * container; + // read folders + for ( Field::FieldListIterator it = contactListFields.find( NM_A_FA_FOLDER ); + it != contactListFields.end(); + it = contactListFields.find( ++it, NM_A_FA_FOLDER ) ) + { + container = static_cast<Field::MultiField *>( *it ); + extractFolder( container ); + } + + // read contacts + for ( Field::FieldListIterator it = contactListFields.find( NM_A_FA_CONTACT ); + it != contactListFields.end(); + it = contactListFields.find( ++it, NM_A_FA_CONTACT ) ) + { + container = static_cast<Field::MultiField *>( *it ); + extractContact( container ); + } + } + + extractKeepalivePeriod( loginResponseFields ); + + setSuccess(); + + return true; +} + +void LoginTask::extractFolder( Field::MultiField * folderContainer ) +{ + FolderItem folder; + Field::SingleField * current; + Field::FieldList fl = folderContainer->fields(); + // object id + current = fl.findSingleField( NM_A_SZ_OBJECT_ID ); + folder.id = current->value().toInt(); + // sequence number + current = fl.findSingleField( NM_A_SZ_SEQUENCE_NUMBER ); + folder.sequence = current->value().toInt(); + // name + current = fl.findSingleField( NM_A_SZ_DISPLAY_NAME ); + folder.name = current->value().toString(); + // parent + current = fl.findSingleField( NM_A_SZ_PARENT_ID ); + folder.parentId = current->value().toInt(); + + client()->debug( QString( "Got folder: %1, obj: %2, parent: %3, seq: %3." ).arg( folder.name ).arg( folder.id ).arg( folder.parentId ).arg( folder.sequence ) ); + // tell the world about it + emit gotFolder( folder ); +} + +void LoginTask::extractContact( Field::MultiField * contactContainer ) +{ + if ( contactContainer->tag() != NM_A_FA_CONTACT ) + return; + ContactItem contact; + Field::SingleField * current; + Field::FieldList fl = contactContainer->fields(); + // sequence number, object and parent IDs are a numeric values but are stored as strings... + current = fl.findSingleField( NM_A_SZ_OBJECT_ID ); + contact.id = current->value().toInt(); + current = fl.findSingleField( NM_A_SZ_PARENT_ID ); + contact.parentId = current->value().toInt(); + current = fl.findSingleField( NM_A_SZ_SEQUENCE_NUMBER ); + contact.sequence = current->value().toInt(); + current = fl.findSingleField( NM_A_SZ_DISPLAY_NAME ); + contact.displayName = current->value().toString(); + current = fl.findSingleField( NM_A_SZ_DN ); + contact.dn = current->value().toString().lower(); + emit gotContact( contact ); + Field::MultiField * details = fl.findMultiField( NM_A_FA_USER_DETAILS ); + if ( details ) // not all contact list contacts have these + { + Field::FieldList detailsFields = details->fields(); + ContactDetails cd = extractUserDetails( detailsFields ); + if ( cd.dn.isEmpty() ) + cd.dn = contact.dn; + // tell the UserDetailsManager that we have this contact's details + client()->userDetailsManager()->addDetails( cd ); + emit gotContactUserDetails( cd ); + } +} + +ContactDetails LoginTask::extractUserDetails( Field::FieldList & fields ) +{ + ContactDetails cd; + cd.status = GroupWise::Invalid; + cd.archive = false; + // read the supplied fields, set metadata and status. + Field::SingleField * sf; + if ( ( sf = fields.findSingleField ( NM_A_SZ_AUTH_ATTRIBUTE ) ) ) + cd.authAttribute = sf->value().toString(); + if ( ( sf = fields.findSingleField ( NM_A_SZ_DN ) ) ) + cd.dn = sf->value().toString().lower(); // HACK: lowercased DN + if ( ( sf = fields.findSingleField ( "CN" ) ) ) + cd.cn = sf->value().toString(); + if ( ( sf = fields.findSingleField ( "Given Name" ) ) ) + cd.givenName = sf->value().toString(); + if ( ( sf = fields.findSingleField ( "Surname" ) ) ) + cd.surname = sf->value().toString(); + if ( ( sf = fields.findSingleField ( "Full Name" ) ) ) + cd.fullName = sf->value().toString(); + if ( ( sf = fields.findSingleField ( "nnmArchive" ) ) ) + cd.archive = ( sf->value().toInt() == 1 ); + if ( ( sf = fields.findSingleField ( NM_A_SZ_STATUS ) ) ) + cd.status = sf->value().toInt(); + if ( ( sf = fields.findSingleField ( NM_A_SZ_MESSAGE_BODY ) ) ) + cd.awayMessage = sf->value().toString(); + Field::MultiField * mf; + QMap< QString, QString > propMap; + if ( ( mf = fields.findMultiField ( NM_A_FA_INFO_DISPLAY_ARRAY ) ) ) + { + Field::FieldList fl = mf->fields(); + const Field::FieldListIterator end = fl.end(); + for ( Field::FieldListIterator it = fl.begin(); it != end; ++it ) + { + Field::SingleField * propField = dynamic_cast<Field::SingleField *>( *it ); + if ( propField ) + { + QString propName = propField->tag(); + QString propValue = propField->value().toString(); + propMap.insert( propName, propValue ); + } + else + { + Field::MultiField * propList = dynamic_cast<Field::MultiField*>( *it ); + if ( propList ) + { + // Hello A Nagappan. GW gave us a multiple field where we previously got a single field + QString parentName = propList->tag(); + Field::FieldList propFields = propList->fields(); + const Field::FieldListIterator end = propFields.end(); + for ( Field::FieldListIterator it = propFields.begin(); it != end; ++it ) + { + propField = dynamic_cast<Field::SingleField *>( *it ); + if ( propField /*&& propField->tag() == parentName */) + { + QString propValue = propField->value().toString(); + QString contents = propMap[ propField->tag() ]; + if ( !contents.isEmpty() ) + contents.append( ", " ); + contents.append( propField->value().toString()); + propMap.insert( propField->tag(), contents ); + } + } + } + } + } + } + if ( !propMap.empty() ) + { + cd.properties = propMap; + } + return cd; +} + +void LoginTask::extractPrivacy( Field::FieldList & fields ) +{ + bool privacyLocked = false; + bool defaultDeny = false; + QStringList allowList; + QStringList denyList; + // read blocking + // may be a single field or may be an array + Field::FieldListIterator it = fields.find( NM_A_LOCKED_ATTR_LIST ); + if ( it != fields.end() ) + { + if ( Field::SingleField * sf = dynamic_cast<Field::SingleField *>( *it ) ) + { + if ( sf->value().toString().find( NM_A_BLOCKING ) ) + privacyLocked = true; + } + else if ( Field::MultiField * mf = dynamic_cast<Field::MultiField *>( *it ) ) + { + Field::FieldList fl = mf->fields(); + for ( Field::FieldListIterator it = fl.begin(); it != fl.end(); ++it ) + { + if ( Field::SingleField * sf = dynamic_cast<Field::SingleField *>( *it ) ) + { + if ( sf->tag() == NM_A_BLOCKING ) + { + privacyLocked = true; + break; + } + } + } + } + } + + // read default privacy policy + Field::SingleField * sf = fields.findSingleField( NM_A_BLOCKING ); + if ( sf ) + defaultDeny = ( sf->value().toInt() != 0 ); + + + // read deny list + denyList = readPrivacyItems( NM_A_BLOCKING_DENY_LIST, fields ); + // read allow list + allowList = readPrivacyItems( NM_A_BLOCKING_ALLOW_LIST, fields ); + emit gotPrivacySettings( privacyLocked, defaultDeny, allowList, denyList ); + kdDebug( GROUPWISE_DEBUG_GLOBAL ) << "locked is " << privacyLocked << ", default is " << defaultDeny << "\nallow list is: " << allowList << "\ndeny list is: " << denyList << endl; +} + +QStringList LoginTask::readPrivacyItems( const QCString & tag, Field::FieldList & fields ) +{ + QStringList items; + + Field::FieldListIterator it = fields.find( tag ); + if ( it != fields.end() ) + { + if ( Field::SingleField * sf = dynamic_cast<Field::SingleField *>( *it ) ) + { + items.append( sf->value().toString().lower() ); + } + else if ( Field::MultiField * mf = dynamic_cast<Field::MultiField *>( *it ) ) + { + Field::FieldList fl = mf->fields(); + for ( Field::FieldListIterator it = fl.begin(); it != fl.end(); ++it ) + { + if ( Field::SingleField * sf = dynamic_cast<Field::SingleField *>( *it ) ) + { + items.append( sf->value().toString().lower() ); + } + } + } + } + return items; +} + +void LoginTask::extractCustomStatuses( Field::FieldList & fields ) +{ + Field::FieldListIterator it = fields.find( NM_A_FA_CUSTOM_STATUSES ); + if ( it != fields.end() ) + { + if ( Field::MultiField * mf = dynamic_cast<Field::MultiField *>( *it ) ) + { + Field::FieldList fl = mf->fields(); + for ( Field::FieldListIterator custStatIt = fl.begin(); custStatIt != fl.end(); ++custStatIt ) + { + Field::MultiField * mf2 = dynamic_cast<Field::MultiField *>( *custStatIt ); + if ( mf2 && ( mf2->tag() == NM_A_FA_STATUS ) ) + { + GroupWise::CustomStatus custom; + Field::FieldList fl2 = mf2->fields(); + for ( Field::FieldListIterator custContentIt = fl2.begin(); custContentIt != fl2.end(); ++custContentIt ) + { + if ( Field::SingleField * sf3 = dynamic_cast<Field::SingleField *>( *custContentIt ) ) + { + if ( sf3->tag() == NM_A_SZ_TYPE ) + custom.status = (GroupWise::Status)sf3->value().toInt(); + else if ( sf3->tag() == NM_A_SZ_DISPLAY_NAME ) + custom.name = sf3->value().toString(); + else if ( sf3->tag() == NM_A_SZ_MESSAGE_BODY ) + custom.autoReply = sf3->value().toString(); + } + } + emit gotCustomStatus( custom ); + } + } + } + } +} + +void LoginTask::extractKeepalivePeriod( Field::FieldList & fields ) +{ + Field::FieldListIterator it = fields.find( NM_A_UD_KEEPALIVE ); + if ( it != fields.end() ) + { + if ( Field::SingleField * sf = dynamic_cast<Field::SingleField *>( *it ) ) + { + bool ok; + int period = sf->value().toInt( &ok ); + if ( ok ) + { + emit gotKeepalivePeriod( period ); + } + } + } +} + +#include "logintask.moc" diff --git a/kopete/protocols/groupwise/libgroupwise/tasks/logintask.h b/kopete/protocols/groupwise/libgroupwise/tasks/logintask.h new file mode 100644 index 00000000..0b2acdfd --- /dev/null +++ b/kopete/protocols/groupwise/libgroupwise/tasks/logintask.h @@ -0,0 +1,64 @@ +/* + Kopete Groupwise Protocol + logintask.h - Send our credentials to the server and process the contact list and privacy details that it returns. + + Copyright (c) 2004 SUSE Linux AG http://www.suse.com + + Based on Iris, Copyright (C) 2003 Justin Karneges + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#ifndef LOGINTASK_H +#define LOGINTASK_H + +#include "requesttask.h" + +using namespace GroupWise; + +/** +@author Kopete Developers +*/ +class LoginTask : public RequestTask +{ +Q_OBJECT +public: + LoginTask( Task * parent ); + ~LoginTask(); + /** + * Get the login fields ready to go + */ + void initialise(); + /** + * Only accepts the contactlist that comes back from the server, + * processes it and notifies the client of the contactlist + */ + bool take( Transfer * transfer ); +protected: + void extractFolder( Field::MultiField * folderContainer ); + void extractContact( Field::MultiField * contactContainer ); + ContactDetails extractUserDetails( Field::FieldList & fields ); + void extractPrivacy( Field::FieldList & fields ); + QStringList readPrivacyItems( const QCString & tag, Field::FieldList & fields ); + void extractCustomStatuses( Field::FieldList & fields ); + void extractKeepalivePeriod( Field::FieldList & fields ); +signals: + void gotMyself( const GroupWise::ContactDetails & ); + void gotFolder( const FolderItem & ); + void gotContact( const ContactItem & ); + void gotContactUserDetails( const GroupWise::ContactDetails & ); + void gotPrivacySettings( bool locked, bool defaultDeny, const QStringList & allowList, const QStringList & denyList ); + void gotCustomStatus( const GroupWise::CustomStatus & ); + void gotKeepalivePeriod( int ); +}; + +#endif diff --git a/kopete/protocols/groupwise/libgroupwise/tasks/modifycontactlisttask.cpp b/kopete/protocols/groupwise/libgroupwise/tasks/modifycontactlisttask.cpp new file mode 100644 index 00000000..10233a18 --- /dev/null +++ b/kopete/protocols/groupwise/libgroupwise/tasks/modifycontactlisttask.cpp @@ -0,0 +1,139 @@ +/* + Kopete Groupwise Protocol + modifycontactlisttask.cpp - Ancestor of all tasks that change the server side contact list. + + Copyright (c) 2004 SUSE Linux AG http://www.suse.com + + Based on Iris, Copyright (C) 2003 Justin Karneges + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#include "client.h" +#include "response.h" +#include "gwerror.h" +#include "modifycontactlisttask.h" + +ModifyContactListTask::ModifyContactListTask(Task* parent): RequestTask(parent) +{ +} + +ModifyContactListTask::~ModifyContactListTask() +{ +} + +bool ModifyContactListTask::take( Transfer * transfer ) +{ + if ( !forMe( transfer ) ) + return false; + Response * response = dynamic_cast<Response *>( transfer ); + if ( !response ) + return false; + client()->debug( "ModifyContactListTask::take()" ); + + // scan the contact list received + // emit each add and delete as a signal + Field::FieldList fl = response->fields(); + fl.dump( true ); + Field::FieldListIterator it = fl.begin(); + Field::FieldListIterator end = fl.end(); + Field::MultiField * current = fl.findMultiField( NM_A_FA_RESULTS ); + if ( current ) + fl = current->fields(); + current = fl.findMultiField( NM_A_FA_CONTACT_LIST ); + if ( current ) + { + Field::FieldList contactList = current->fields(); + Field::FieldListIterator cursor = contactList.begin(); + const Field::FieldListIterator end = contactList.end(); + while ( cursor != end ) + { + Field::MultiField * mf = dynamic_cast< Field::MultiField * >( *cursor ); + if ( mf->tag() == NM_A_FA_CONTACT ) + { + // contact change + processContactChange( mf ); + } + else if ( mf->tag() == NM_A_FA_FOLDER ) + { + // folder change + processFolderChange( mf ); + } + ++cursor; + } + } + // TODO: call virtual here to read any fields after the contact list... + if ( response->resultCode() == GroupWise::None ) + setSuccess(); + else + setError( response->resultCode() ); + return true; +} + +void ModifyContactListTask::processContactChange( Field::MultiField * container ) +{ + if ( !( container->method() == NMFIELD_METHOD_ADD + || container->method() == NMFIELD_METHOD_DELETE ) ) + return; + + client()->debug( "ModifyContactListTask::processContactChange()" ); + Field::SingleField * current; + Field::FieldList fl = container->fields(); + ContactItem contact; + current = fl.findSingleField( NM_A_SZ_OBJECT_ID ); + contact.id = current->value().toInt(); + current = fl.findSingleField( NM_A_SZ_PARENT_ID ); + contact.parentId = current->value().toInt(); + current = fl.findSingleField( NM_A_SZ_SEQUENCE_NUMBER ); + contact.sequence = current->value().toInt(); + current = fl.findSingleField( NM_A_SZ_DISPLAY_NAME ); + contact.displayName = current->value().toString(); + current = fl.findSingleField( NM_A_SZ_DN ); + contact.dn = current->value().toString(); + + if ( container->method() == NMFIELD_METHOD_ADD ) + emit gotContactAdded( contact ); + else if ( container->method() == NMFIELD_METHOD_DELETE ) + emit gotContactDeleted( contact ); +} + +void ModifyContactListTask::processFolderChange( Field::MultiField * container ) +{ + if ( !( container->method() == NMFIELD_METHOD_ADD + || container->method() == NMFIELD_METHOD_DELETE ) ) + return; + + client()->debug( "ModifyContactListTask::processFolderChange()" ); + FolderItem folder; + Field::SingleField * current; + Field::FieldList fl = container->fields(); + // object id + current = fl.findSingleField( NM_A_SZ_OBJECT_ID ); + folder.id = current->value().toInt(); + // sequence number + current = fl.findSingleField( NM_A_SZ_SEQUENCE_NUMBER ); + folder.sequence = current->value().toInt(); + // name + current = fl.findSingleField( NM_A_SZ_DISPLAY_NAME ); + folder.name = current->value().toString(); + // parent + current = fl.findSingleField( NM_A_SZ_PARENT_ID ); + folder.parentId = current->value().toInt(); + if ( container->method() == NMFIELD_METHOD_ADD ) + emit gotFolderAdded( folder ); + else if ( container->method() == NMFIELD_METHOD_DELETE ) + emit gotFolderDeleted( folder ); + +} + + +#include "modifycontactlisttask.moc" diff --git a/kopete/protocols/groupwise/libgroupwise/tasks/modifycontactlisttask.h b/kopete/protocols/groupwise/libgroupwise/tasks/modifycontactlisttask.h new file mode 100644 index 00000000..2f5a4939 --- /dev/null +++ b/kopete/protocols/groupwise/libgroupwise/tasks/modifycontactlisttask.h @@ -0,0 +1,51 @@ +/* + Kopete Groupwise Protocol + modifycontactlisttask.h - Ancestor of all tasks that change the server side contact list. + + Copyright (c) 2004 SUSE Linux AG http://www.suse.com + + Based on Iris, Copyright (C) 2003 Justin Karneges + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#ifndef MODIFYCONTACTLISTTASK_H +#define MODIFYCONTACTLISTTASK_H + +#include "requesttask.h" + +/** +This is the parent of all tasks that manipulate the contact list. The server responds to each one in the same way, and this task contains a take() to process this response. + +@author SUSE AG +*/ + +using namespace GroupWise; + +class ModifyContactListTask : public RequestTask +{ +Q_OBJECT +public: + ModifyContactListTask(Task* parent); + ~ModifyContactListTask(); + bool take( Transfer * transfer ); +signals: + void gotFolderAdded( const FolderItem &); + void gotFolderDeleted( const FolderItem & ); + void gotContactAdded( const ContactItem & ); + void gotContactDeleted( const ContactItem & ); +private: + void processFolderChange( Field::MultiField * container ); + void processContactChange( Field::MultiField * container ); +}; + +#endif diff --git a/kopete/protocols/groupwise/libgroupwise/tasks/movecontacttask.cpp b/kopete/protocols/groupwise/libgroupwise/tasks/movecontacttask.cpp new file mode 100644 index 00000000..713315ee --- /dev/null +++ b/kopete/protocols/groupwise/libgroupwise/tasks/movecontacttask.cpp @@ -0,0 +1,83 @@ +/* + Kopete Groupwise Protocol + movecontacttask.cpp - Move a contact between folders on the server + + Copyright (c) 2004 SUSE Linux AG http://www.suse.com + + Based on Iris, Copyright (C) 2003 Justin Karneges + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#include "client.h" + +#include "movecontacttask.h" + +MoveContactTask::MoveContactTask(Task* parent): NeedFolderTask(parent) +{ + // make the client tell the client app (Kopete) when we receive a contact + connect( this, SIGNAL( gotContactAdded( const ContactItem & ) ), client(), SIGNAL( contactReceived( const ContactItem & ) ) ); +} + + +MoveContactTask::~MoveContactTask() +{ +} + +void MoveContactTask::moveContact( const ContactItem & contact, const int newParent ) +{ + Field::FieldList lst; + // TODO: - write a contact_item_to_fields method and factor duplicate code like this out + Field::FieldList contactFields; + contactFields.append( new Field::SingleField( NM_A_SZ_OBJECT_ID, 0, NMFIELD_TYPE_UTF8, contact.id ) ); + contactFields.append( new Field::SingleField( NM_A_SZ_PARENT_ID, 0, NMFIELD_TYPE_UTF8, contact.parentId ) ); + contactFields.append( new Field::SingleField( NM_A_SZ_SEQUENCE_NUMBER, 0, NMFIELD_TYPE_UTF8, contact.sequence ) ); + if ( !contact.dn.isNull() ) + contactFields.append( new Field::SingleField( NM_A_SZ_DN, 0, NMFIELD_TYPE_UTF8, contact.dn ) ); + if ( !contact.displayName.isNull() ) + contactFields.append( new Field::SingleField( NM_A_SZ_DISPLAY_NAME, 0, NMFIELD_TYPE_UTF8, contact.displayName ) ); + Field::FieldList contactList; + contactList.append( + new Field::MultiField( NM_A_FA_CONTACT, NMFIELD_METHOD_DELETE, 0, NMFIELD_TYPE_ARRAY, contactFields ) ); + + lst.append( new Field::MultiField( NM_A_FA_CONTACT_LIST, NMFIELD_METHOD_VALID, 0, NMFIELD_TYPE_ARRAY, contactList ) ); + + lst.append( new Field::SingleField( NM_A_SZ_SEQUENCE_NUMBER, 0, NMFIELD_TYPE_UTF8, "-1" ) ); + lst.append( new Field::SingleField( NM_A_SZ_PARENT_ID, 0, NMFIELD_TYPE_UTF8, QString::number( newParent ) ) ); + createTransfer( "movecontact", lst ); +} + +void MoveContactTask::moveContactToNewFolder( const ContactItem & contact, const int newSequenceNumber, const QString & folderDisplayName ) +{ + client()->debug("MoveContactTask::moveContactToNewFolder()" ); + m_folderSequence = newSequenceNumber; + m_folderDisplayName = folderDisplayName; + m_contactToMove = contact; + +} + +void MoveContactTask::onGo() +{ + // are we creating a folder first or can we just proceed as normal? + if ( m_folderDisplayName.isEmpty() ) + RequestTask::onGo(); + else // create the folder, when the folder has been created, onFolderCreated gets called and creates the contact + createFolder(); +} + +void MoveContactTask::onFolderCreated() +{ + client()->debug("MoveContactTask::onFolderCreated()" ); + moveContact( m_contactToMove, m_folderId ); + RequestTask::onGo(); +} +#include "movecontacttask.moc" diff --git a/kopete/protocols/groupwise/libgroupwise/tasks/movecontacttask.h b/kopete/protocols/groupwise/libgroupwise/tasks/movecontacttask.h new file mode 100644 index 00000000..f423981a --- /dev/null +++ b/kopete/protocols/groupwise/libgroupwise/tasks/movecontacttask.h @@ -0,0 +1,49 @@ +/* + Kopete Groupwise Protocol + movecontacttask.h - Move a contact between folders on the server + + Copyright (c) 2004 SUSE Linux AG http://www.suse.com + + Based on Iris, Copyright (C) 2003 Justin Karneges + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#ifndef MOVECONTACTTASK_H +#define MOVECONTACTTASK_H + +#include "needfoldertask.h" + +/** +Moves a contact between folders on the server + +@author SUSE AG +*/ +class MoveContactTask : public NeedFolderTask +{ +Q_OBJECT +public: + MoveContactTask(Task* parent); + ~MoveContactTask(); + void moveContact( const ContactItem & contact, const int newParent ); + void moveContactToNewFolder( const ContactItem & contact, const int newSequenceNumber, const QString & folderDisplayName ); + void onGo(); +protected: + void onFolderCreated(); +private: + int m_targetFolder; + QString m_dn; + QString m_displayName; + ContactItem m_contactToMove; +}; + +#endif diff --git a/kopete/protocols/groupwise/libgroupwise/tasks/needfoldertask.cpp b/kopete/protocols/groupwise/libgroupwise/tasks/needfoldertask.cpp new file mode 100644 index 00000000..810326ee --- /dev/null +++ b/kopete/protocols/groupwise/libgroupwise/tasks/needfoldertask.cpp @@ -0,0 +1,58 @@ +// +// C++ Implementation: %{MODULE} +// +// Description: +// +// +// Author: %{AUTHOR} <%{EMAIL}>, (C) %{YEAR} +// +// Copyright: See COPYING file that comes with this distribution +// +// +#include "client.h" +#include "tasks/createcontactinstancetask.h" +#include "tasks/createfoldertask.h" + +#include "needfoldertask.h" + +NeedFolderTask::NeedFolderTask(Task* parent): ModifyContactListTask(parent) +{ +} + +NeedFolderTask::~NeedFolderTask() +{ +} + +void NeedFolderTask::createFolder() +{ + CreateFolderTask * cct = new CreateFolderTask( client()->rootTask() ); + cct->folder( 0, m_folderSequence, m_folderDisplayName ); + connect( cct, SIGNAL( gotFolderAdded( const FolderItem & ) ), client(), SIGNAL( folderReceived( const FolderItem & ) ) ); + connect( cct, SIGNAL( gotFolderAdded( const FolderItem & ) ), SLOT( slotFolderAdded( const FolderItem & ) ) ); + connect( cct, SIGNAL( finished() ), SLOT( slotFolderTaskFinished() ) ); + cct->go( true ); +} + +void NeedFolderTask::slotFolderAdded( const FolderItem & addedFolder ) +{ + // if this is the folder we were trying to create + if ( m_folderDisplayName == addedFolder.name ) + { + client()->debug( QString( "NeedFolderTask::slotFolderAdded() - Folder %1 was created on the server, now has objectId %2" ).arg( addedFolder.name ).arg( addedFolder.id ) ); + m_folderId = addedFolder.id; + } +} + +void NeedFolderTask::slotFolderTaskFinished() +{ + CreateFolderTask *cct = ( CreateFolderTask* )sender(); + if ( cct->success() ) + { + // call our child class's action to be performed + onFolderCreated(); + } + else + setError( 1, "Folder creation failed" ); +} + +#include "needfoldertask.moc" diff --git a/kopete/protocols/groupwise/libgroupwise/tasks/needfoldertask.h b/kopete/protocols/groupwise/libgroupwise/tasks/needfoldertask.h new file mode 100644 index 00000000..8d6278df --- /dev/null +++ b/kopete/protocols/groupwise/libgroupwise/tasks/needfoldertask.h @@ -0,0 +1,39 @@ +// +// C++ Interface: %{MODULE} +// +// Description: +// +// +// Author: %{AUTHOR} <%{EMAIL}>, (C) %{YEAR} +// +// Copyright: See COPYING file that comes with this distribution +// +// +#ifndef NEEDFOLDERTASK_H +#define NEEDFOLDERTASK_H + +#include "modifycontactlisttask.h" + +/** +This Task is the ancestor of Tasks that may need to create a folder on the server before they can carry out their own operation. + +@author Kopete Developers +*/ +class NeedFolderTask : public ModifyContactListTask +{ +Q_OBJECT +public: + NeedFolderTask(Task* parent); + ~NeedFolderTask(); + void createFolder(); + virtual void onFolderCreated() = 0; +protected slots: + void slotFolderAdded( const FolderItem & ); + void slotFolderTaskFinished(); +protected: + int m_folderSequence; + int m_folderId; + QString m_folderDisplayName; +}; + +#endif diff --git a/kopete/protocols/groupwise/libgroupwise/tasks/pollsearchresultstask.cpp b/kopete/protocols/groupwise/libgroupwise/tasks/pollsearchresultstask.cpp new file mode 100644 index 00000000..772a0888 --- /dev/null +++ b/kopete/protocols/groupwise/libgroupwise/tasks/pollsearchresultstask.cpp @@ -0,0 +1,185 @@ +/* + Kopete Groupwise Protocol + pollsearchresultstask.cpp - Poll the server to see if it has processed our search yet. + + Copyright (c) 2004 SUSE Linux AG http://www.suse.com + + Based on Iris, Copyright (C) 2003 Justin Karneges + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#include "gwfield.h" +#include "response.h" + +#include "logintask.h" + +#include "pollsearchresultstask.h" + +using namespace GroupWise; + +PollSearchResultsTask::PollSearchResultsTask(Task* parent): RequestTask(parent) +{ +} + + +PollSearchResultsTask::~PollSearchResultsTask() +{ +} + +void PollSearchResultsTask::poll( const QString & queryHandle ) +{ + Field::FieldList lst; + lst.append( new Field::SingleField( NM_A_SZ_OBJECT_ID, 0, NMFIELD_TYPE_UTF8, queryHandle ) ); + createTransfer( "getresults", lst ); +} + +bool PollSearchResultsTask::take( Transfer * transfer ) +{ + if ( !forMe( transfer ) ) + return false; + Response * response = dynamic_cast<Response *>( transfer ); + if ( !response ) + return false; + if ( response->resultCode() ) + { + setError( response->resultCode() ); + return true; + } + + // look for the status code + Field::FieldList responseFields = response->fields(); + Field::SingleField * sf = responseFields.findSingleField( NM_A_SZ_STATUS ); + m_queryStatus = sf->value().toInt(); + + Field::MultiField * resultsArray = responseFields.findMultiField( NM_A_FA_RESULTS ); + if ( !resultsArray ) + { + setError( Protocol ); + return true; + } + Field::FieldList matches = resultsArray->fields(); + const Field::FieldListIterator end = matches.end(); + for ( Field::FieldListIterator it = matches.find( NM_A_FA_CONTACT ); + it != end; + it = matches.find( ++it, NM_A_FA_CONTACT ) ) + { + Field::MultiField * mf = static_cast<Field::MultiField *>( *it ); + Field::FieldList contact = mf->fields(); + GroupWise::ContactDetails cd = extractUserDetails( contact ); + m_results.append( cd ); + } + + // first field: NM_A_SZ_STATUS contains + #define SEARCH_PENDING 0 + #define SEARCH_INPROGRESS 1 + #define SEARCH_COMPLETED 2 + #define SEARCH_TIMEOUT 3 + #define SEARCH_CANCELLED 4 + #define SEARCH_ERROR 5 + // set a status code if needed + // followed by NM_A_FA_RESULTS, looks like a getdetails + // add an accessor to get at the results list of ContactItems, probably + + if ( m_queryStatus != 2 ) + setError( m_queryStatus ); + else + setSuccess( m_queryStatus ); + return true; +} + +QValueList< GroupWise::ContactDetails > PollSearchResultsTask::results() +{ + return m_results; +} + +int PollSearchResultsTask::queryStatus() +{ + return m_queryStatus; +} + +GroupWise::ContactDetails PollSearchResultsTask::extractUserDetails( Field::FieldList & fields ) +{ + ContactDetails cd; + cd.status = GroupWise::Invalid; + cd.archive = false; + // read the supplied fields, set metadata and status. + Field::SingleField * sf; + if ( ( sf = fields.findSingleField ( NM_A_SZ_AUTH_ATTRIBUTE ) ) ) + cd.authAttribute = sf->value().toString(); + if ( ( sf = fields.findSingleField ( NM_A_SZ_DN ) ) ) + cd.dn =sf->value().toString().lower(); // HACK: lowercased DN + if ( ( sf = fields.findSingleField ( "CN" ) ) ) + cd.cn = sf->value().toString(); + if ( ( sf = fields.findSingleField ( "Given Name" ) ) ) + cd.givenName = sf->value().toString(); + if ( ( sf = fields.findSingleField ( "Surname" ) ) ) + cd.surname = sf->value().toString(); + if ( ( sf = fields.findSingleField ( "Full Name" ) ) ) + cd.fullName = sf->value().toString(); + if ( ( sf = fields.findSingleField ( "nnmArchive" ) ) ) + cd.archive = ( sf->value().toInt() == 1 ); + if ( ( sf = fields.findSingleField ( NM_A_SZ_STATUS ) ) ) + cd.status = sf->value().toInt(); + if ( ( sf = fields.findSingleField ( NM_A_SZ_MESSAGE_BODY ) ) ) + cd.awayMessage = sf->value().toString(); + Field::MultiField * mf; + QMap< QString, QString > propMap; + if ( ( mf = fields.findMultiField ( NM_A_FA_INFO_DISPLAY_ARRAY ) ) ) + { + Field::FieldList fl = mf->fields(); + const Field::FieldListIterator end = fl.end(); + for ( Field::FieldListIterator it = fl.begin(); it != end; ++it ) + { + // assumes each property only present once + // check in logintask.cpp and if it's a multi field, + // get the value of this instance, check if it's already in the property map and append if found. + Field::SingleField * propField = dynamic_cast<Field::SingleField *>( *it ); + if ( propField ) + { + QString propName = propField->tag(); + QString propValue = propField->value().toString(); + propMap.insert( propName, propValue ); + } + else + { + Field::MultiField * propList = dynamic_cast<Field::MultiField*>( *it ); + if ( propList ) + { + QString parentName = propList->tag(); + Field::FieldList propFields = propList->fields(); + const Field::FieldListIterator end = propFields.end(); + for ( Field::FieldListIterator it = propFields.begin(); it != end; ++it ) + { + propField = dynamic_cast<Field::SingleField *>( *it ); + if ( propField ) + { + QString propValue = propField->value().toString(); + QString contents = propMap[ propField->tag() ]; + if ( !contents.isEmpty() ) + contents.append( ", " ); + contents.append( propField->value().toString()); + propMap.insert( propField->tag(), contents ); + } + } + } + } + } + } + if ( !propMap.empty() ) + { + cd.properties = propMap; + } + return cd; +} + +#include "pollsearchresultstask.moc" diff --git a/kopete/protocols/groupwise/libgroupwise/tasks/pollsearchresultstask.h b/kopete/protocols/groupwise/libgroupwise/tasks/pollsearchresultstask.h new file mode 100644 index 00000000..11f810c0 --- /dev/null +++ b/kopete/protocols/groupwise/libgroupwise/tasks/pollsearchresultstask.h @@ -0,0 +1,52 @@ +/* + Kopete Groupwise Protocol + pollsearchresultstask.h - Poll the server once to see if it has processed our search yet. + + Copyright (c) 2004 SUSE Linux AG http://www.suse.com + + Based on Iris, Copyright (C) 2003 Justin Karneges + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#ifndef POLLSEARCHRESULTSTASK_H +#define POLLSEARCHRESULTSTASK_H + +#include <qvaluelist.h> + +#include "gwerror.h" + +#include "requesttask.h" + +/** +Search results are polled on the server, using the search handle supplied by the client with the original query. This is a single poll request, which if successful, will retrieve the results. Otherwise, it will set a status code, so the ContactSearchTask can decide whether to poll again. + +@author SUSE AG +*/ +class PollSearchResultsTask : public RequestTask +{ +Q_OBJECT +public: + enum SearchResultCode { Pending=0, InProgess=1, Completed=2, TimeOut=3, Cancelled=4, Error=5 }; + PollSearchResultsTask(Task* parent); + ~PollSearchResultsTask(); + void poll( const QString & queryHandle); + bool take( Transfer * transfer ); + int queryStatus(); + QValueList< GroupWise::ContactDetails > results(); +GroupWise::ContactDetails extractUserDetails( Field::FieldList & fields ); +private: + int m_queryStatus; + QValueList< GroupWise::ContactDetails > m_results; +}; + +#endif diff --git a/kopete/protocols/groupwise/libgroupwise/tasks/privacyitemtask.cpp b/kopete/protocols/groupwise/libgroupwise/tasks/privacyitemtask.cpp new file mode 100644 index 00000000..003a6d60 --- /dev/null +++ b/kopete/protocols/groupwise/libgroupwise/tasks/privacyitemtask.cpp @@ -0,0 +1,82 @@ +/* + Kopete Groupwise Protocol + privacyitemtask.cpp - Add an entry to the server side deny or allow lists + + Copyright (c) 2004 SUSE Linux AG http://www.suse.com + + Based on Iris, Copyright (C) 2003 Justin Karneges + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#include "privacyitemtask.h" + +PrivacyItemTask::PrivacyItemTask( Task* parent) : RequestTask( parent ) +{ +} + +PrivacyItemTask::~PrivacyItemTask() +{ +} + +QString PrivacyItemTask::dn() const +{ + return m_dn; +} + +bool PrivacyItemTask::defaultDeny() const +{ + return m_default; +} + +void PrivacyItemTask::allow( const QString & dn ) +{ + m_dn = dn; + Field::FieldList lst; + lst.append( new Field::SingleField( NM_A_SZ_BLOCKING_ALLOW_ITEM, NMFIELD_METHOD_ADD, 0, NMFIELD_TYPE_UTF8, dn ) ); + createTransfer( "createblock", lst ); +} + +void PrivacyItemTask::deny( const QString & dn ) +{ + m_dn = dn; + Field::FieldList lst; + lst.append( new Field::SingleField( NM_A_SZ_BLOCKING_DENY_ITEM, NMFIELD_METHOD_ADD, 0, NMFIELD_TYPE_UTF8, dn ) ); + createTransfer( "createblock", lst ); +} + +void PrivacyItemTask::removeAllow( const QString & dn ) +{ + m_dn = dn; + Field::FieldList lst; + lst.append( new Field::SingleField( NM_A_BLOCKING_ALLOW_LIST, NMFIELD_METHOD_DELETE, 0, NMFIELD_TYPE_UTF8, dn ) ); + createTransfer( "updateblocks", lst ); + +} + +void PrivacyItemTask::removeDeny( const QString & dn ) +{ + m_dn = dn; + Field::FieldList lst; + lst.append( new Field::SingleField( NM_A_BLOCKING_DENY_LIST, NMFIELD_METHOD_DELETE, 0, NMFIELD_TYPE_UTF8, dn ) ); + createTransfer( "updateblocks", lst ); +} + +void PrivacyItemTask::defaultPolicy( bool defaultDeny ) +{ + m_default = defaultDeny; + Field::FieldList lst; + lst.append( new Field::SingleField( NM_A_BLOCKING, NMFIELD_METHOD_UPDATE, 0, NMFIELD_TYPE_UTF8, ( defaultDeny ? "1" :"0" ) ) ); + createTransfer( "updateblocks", lst ); +} + +#include "privacyitemtask.moc" diff --git a/kopete/protocols/groupwise/libgroupwise/tasks/privacyitemtask.h b/kopete/protocols/groupwise/libgroupwise/tasks/privacyitemtask.h new file mode 100644 index 00000000..809cb7a4 --- /dev/null +++ b/kopete/protocols/groupwise/libgroupwise/tasks/privacyitemtask.h @@ -0,0 +1,50 @@ +/* + Kopete Groupwise Protocol + privacyitemtask.h - Add an entry to the server side deny or allow lists + + Copyright (c) 2004 SUSE Linux AG http://www.suse.com + + Based on Iris, Copyright (C) 2003 Justin Karneges + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#ifndef PRIVACYITEMTASK_H +#define PRIVACYITEMTASK_H + +#include "requesttask.h" + +/** +Adds a contact to the server side allow or deny lists + +@author SUSE AG +*/ +class PrivacyItemTask : public RequestTask +{ +Q_OBJECT +public: + PrivacyItemTask( Task* parent); + ~PrivacyItemTask(); + void allow( const QString & dn ); + void deny( const QString & dn ); + void removeAllow( const QString & dn ); + void removeDeny( const QString & dn ); + void defaultPolicy( bool defaultDeny ); + QString dn() const; + bool defaultDeny() const; + // void contacts( const QStringList & contacts ); +private: + bool m_default; + QString m_dn; +}; + +#endif diff --git a/kopete/protocols/groupwise/libgroupwise/tasks/rejectinvitetask.cpp b/kopete/protocols/groupwise/libgroupwise/tasks/rejectinvitetask.cpp new file mode 100644 index 00000000..2b252ff5 --- /dev/null +++ b/kopete/protocols/groupwise/libgroupwise/tasks/rejectinvitetask.cpp @@ -0,0 +1,39 @@ +/* + Kopete Groupwise Protocol + rejectinvitetask.cpp - Decline an invitation to chat + + Copyright (c) 2004 SUSE Linux AG http://www.suse.com + + Based on Iris, Copyright (C) 2003 Justin Karneges + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#include "rejectinvitetask.h" + +RejectInviteTask::RejectInviteTask(Task* parent): RequestTask(parent) +{ +} + +RejectInviteTask::~RejectInviteTask() +{ +} + +void RejectInviteTask::reject( const GroupWise::ConferenceGuid & guid ) +{ + Field::FieldList lst, tmp; + tmp.append( new Field::SingleField( NM_A_SZ_OBJECT_ID, 0, NMFIELD_TYPE_UTF8, guid ) ); + lst.append( new Field::MultiField( NM_A_FA_CONVERSATION, NMFIELD_METHOD_VALID, 0, NMFIELD_TYPE_ARRAY, tmp ) ); + createTransfer( "rejectconf", lst ); +} + +#include "rejectinvitetask.moc" diff --git a/kopete/protocols/groupwise/libgroupwise/tasks/rejectinvitetask.h b/kopete/protocols/groupwise/libgroupwise/tasks/rejectinvitetask.h new file mode 100644 index 00000000..b82f4e77 --- /dev/null +++ b/kopete/protocols/groupwise/libgroupwise/tasks/rejectinvitetask.h @@ -0,0 +1,41 @@ +/* + Kopete Groupwise Protocol + rejectinvitetask.h - Decline an invitation to chat + + Copyright (c) 2004 SUSE Linux AG http://www.suse.com + + Based on Iris, Copyright (C) 2003 Justin Karneges + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#ifndef REJECTINVITETASK_H +#define REJECTINVITETASK_H + +#include "requesttask.h" + +/** +Used to reject an invitation to join a conference + +@author SUSE AG +*/ +class RejectInviteTask : public RequestTask +{ +Q_OBJECT +public: + RejectInviteTask(Task* parent); + ~RejectInviteTask(); + void reject( const GroupWise::ConferenceGuid & guid ); + +}; + +#endif diff --git a/kopete/protocols/groupwise/libgroupwise/tasks/requesttask.cpp b/kopete/protocols/groupwise/libgroupwise/tasks/requesttask.cpp new file mode 100644 index 00000000..3788bb6e --- /dev/null +++ b/kopete/protocols/groupwise/libgroupwise/tasks/requesttask.cpp @@ -0,0 +1,76 @@ +/* + Kopete Groupwise Protocol + requesttask.cpp - Ancestor of all tasks that carry out a user request + + Copyright (c) 2004 SUSE Linux AG http://www.suse.com + + Based on Iris, Copyright (C) 2003 Justin Karneges + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#include "gwfield.h" +#include "client.h" +#include "request.h" +#include "response.h" +#include "requestfactory.h" + +#include "requesttask.h" + +RequestTask::RequestTask( Task * parent ) +: Task( parent ) +{ +} + +bool RequestTask::forMe( Transfer * transfer ) const +{ + // see if we can down-cast transfer to a Response + Response * theResponse = dynamic_cast<Response *>(transfer); + return (theResponse && theResponse->transactionId() == m_transactionId ); +} + +void RequestTask::createTransfer( const QString & command, const Field::FieldList & fields ) +{ + Request * request = client()->requestFactory()->request( command ); + m_transactionId = request->transactionId(); + request->setFields( fields ); + Task::setTransfer( request ); +} + +void RequestTask::onGo() +{ + if ( transfer() ) + { + client()->debug( QString( "%1::onGo() - sending %2 fields" ).arg( className() ).arg( static_cast<Request *>( transfer() )->command() ) ); + send( static_cast<Request *>( transfer() ) ); + } + else + client()->debug( "RequestTask::onGo() - called prematurely, no transfer set." ); +} + +bool RequestTask::take( Transfer * transfer ) +{ + if ( forMe( transfer ) ) + { + client()->debug( "RequestTask::take() - Default take() Accepting transaction ack, taking no further action" ); + Response * response = dynamic_cast<Response *>( transfer ); + if ( response->resultCode() == GroupWise::None ) + setSuccess(); + else + setError( response->resultCode() ); + return true; + } + else + return false; +} + +#include "requesttask.moc" diff --git a/kopete/protocols/groupwise/libgroupwise/tasks/requesttask.h b/kopete/protocols/groupwise/libgroupwise/tasks/requesttask.h new file mode 100644 index 00000000..30ee57ed --- /dev/null +++ b/kopete/protocols/groupwise/libgroupwise/tasks/requesttask.h @@ -0,0 +1,42 @@ +/* + Kopete Groupwise Protocol + requesttask.h - Ancestor of all tasks that carry out a user request + + Copyright (c) 2004 SUSE Linux AG http://www.suse.com + + Based on Iris, Copyright (C) 2003 Justin Karneges + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#ifndef GW_REQUESTTASK_H +#define GW_REQUESTTASK_H + +#include "task.h" + +class Transfer; + +class RequestTask : public Task +{ +Q_OBJECT + public: + RequestTask( Task *parent ); + bool take( Transfer * transfer ); + virtual void onGo(); + protected: + bool forMe( Transfer * transfer ) const; + void createTransfer( const QString & command, const Field::FieldList & fields ); + private: + int m_transactionId; +}; + +#endif diff --git a/kopete/protocols/groupwise/libgroupwise/tasks/searchchattask.cpp b/kopete/protocols/groupwise/libgroupwise/tasks/searchchattask.cpp new file mode 100644 index 00000000..4ee35549 --- /dev/null +++ b/kopete/protocols/groupwise/libgroupwise/tasks/searchchattask.cpp @@ -0,0 +1,127 @@ +/* + Kopete Groupwise Protocol + searchchattask.cpp - high level search for users on the server - spawns PollSearchResultsTasks + + Copyright (c) 2005 SUSE Linux Products GmbH http://www.suse.com + + Based on Iris, Copyright (C) 2003 Justin Karneges + + Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#include <qdatetime.h> +#include <qtimer.h> + +#include "client.h" +#include "gwerror.h" +#include "gwfield.h" +#include "response.h" + +#include "getchatsearchresultstask.h" + +#include "searchchattask.h" + + +// the delay we allow the server to initially do the search +#define GW_POLL_INITIAL_DELAY 1000 +// the maximum number of times to poll the server +#define GW_POLL_MAXIMUM 5 +// the frequency between subsequent polls +#define GW_POLL_FREQUENCY_MS 8000 + +using namespace GroupWise; + +SearchChatTask::SearchChatTask(Task* parent): RequestTask(parent), m_polls( 0 ) +{ +} + + +SearchChatTask::~SearchChatTask() +{ +} + +void SearchChatTask::search( SearchType type ) +{ + Field::FieldList lst; + // object Id identifies the search for later reference + lst.append( new Field::SingleField( NM_A_B_ONLY_MODIFIED, 0, NMFIELD_TYPE_BOOL, ( type == FetchAll ? 0 : 1 ) ) ); + createTransfer( "chatsearch", lst ); +} + +bool SearchChatTask::take( Transfer * transfer ) +{ + if ( !forMe( transfer ) ) + return false; + Response * response = dynamic_cast<Response *>( transfer ); + if ( !response ) + return false; + if ( response->resultCode() ) + { + kdDebug( GROUPWISE_DEBUG_GLOBAL ) << k_funcinfo << "got return code in response << " << response->resultCode() << endl; + setError( response->resultCode() ); + return true; + } + Field::FieldList responseFields = response->fields(); + Field::SingleField * sf = responseFields.findSingleField( NM_A_UD_OBJECT_ID ); + m_objectId = sf->value().toInt(); + + // now start the results poll timer + QTimer::singleShot( GW_POLL_INITIAL_DELAY, this, SLOT( slotPollForResults() ) ); + return true; +} + +void SearchChatTask::slotPollForResults() +{ + //create a PollSearchResultsTask + GetChatSearchResultsTask * gcsrt = new GetChatSearchResultsTask( client()->rootTask() ); + gcsrt->poll( m_objectId ); + connect( gcsrt, SIGNAL( finished() ), SLOT( slotGotPollResults() ) ); + gcsrt->go( true ); +} + +void SearchChatTask::slotGotPollResults() +{ + GetChatSearchResultsTask * gcsrt = (GetChatSearchResultsTask *)sender(); + kdDebug( GROUPWISE_DEBUG_GLOBAL ) << k_funcinfo << "status code is " << gcsrt->queryStatus() << endl; + m_polls++; + switch ( gcsrt->queryStatus() ) + { + case GetChatSearchResultsTask::GettingData: + if ( m_polls < GW_POLL_MAXIMUM ) // restart timer + QTimer::singleShot( GW_POLL_FREQUENCY_MS, this, SLOT( slotPollForResults() ) ); + else + setSuccess( gcsrt->statusCode() ); + break; + case GetChatSearchResultsTask::DataRetrieved: + // got some results, there may be more. + m_results += gcsrt->results(); + QTimer::singleShot( 0, this, SLOT( slotPollForResults() ) ); + break; + case GetChatSearchResultsTask::Completed: + m_results += gcsrt->results(); + setSuccess(); + break; + case GetChatSearchResultsTask::Cancelled: + setError(gcsrt->statusCode() ); + break; + case GetChatSearchResultsTask::Error: + setError( gcsrt->statusCode() ); + break; + } +} + +QValueList< GroupWise::ChatroomSearchResult > SearchChatTask::results() +{ + return m_results; +} + +#include "searchchattask.moc" diff --git a/kopete/protocols/groupwise/libgroupwise/tasks/searchchattask.h b/kopete/protocols/groupwise/libgroupwise/tasks/searchchattask.h new file mode 100644 index 00000000..2f24e075 --- /dev/null +++ b/kopete/protocols/groupwise/libgroupwise/tasks/searchchattask.h @@ -0,0 +1,66 @@ +/* + Kopete Groupwise Protocol + searchchattask.h - search for chatrooms on the server - spawns PollSearchResultsTasks + + Copyright (c) 2005 SUSE Linux Products GmbH http://www.suse.com + + Based on Iris, Copyright (C) 2003 Justin Karneges + + Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#ifndef SEARCHCHATTASK_H +#define SEARCHCHATTASK_H + +#include "gwerror.h" + +#include "requesttask.h" + +class QTimer; + +/** +This Task searches for chatrooms on the server + +@author SUSE Linux Products GmbH + */ +class SearchChatTask : public RequestTask +{ + Q_OBJECT + public: + enum SearchType { FetchAll=0, SinceLastSearch }; + + SearchChatTask(Task* parent); + + ~SearchChatTask(); + /** + * Create the search query + */ + void search( SearchType type ); + /** + * If the query was accepted, start a timer to poll for results using PollSearchResultsTask + */ + virtual bool take( Transfer * transfer ); + /** + * Access the results of the search + */ + QValueList< GroupWise::ChatroomSearchResult > results(); + protected slots: + void slotPollForResults(); + void slotGotPollResults(); + private: + QTimer * m_resultsPollTimer; + QValueList< GroupWise::ChatroomSearchResult > m_results; + int m_polls; + int m_objectId; // used to identify our query to the server, so we can poll for its results +}; + +#endif diff --git a/kopete/protocols/groupwise/libgroupwise/tasks/searchusertask.cpp b/kopete/protocols/groupwise/libgroupwise/tasks/searchusertask.cpp new file mode 100644 index 00000000..cd199ad8 --- /dev/null +++ b/kopete/protocols/groupwise/libgroupwise/tasks/searchusertask.cpp @@ -0,0 +1,137 @@ +/* + Kopete Groupwise Protocol + searchusertask.cpp - high level search for users on the server - spawns PollSearchResultsTasks + + Copyright (c) 2005 SUSE Linux Products GmbH http://www.suse.com + + Based on Iris, Copyright (C) 2003 Justin Karneges + + Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#include <qdatetime.h> +#include <qtimer.h> + +#include "client.h" +#include "gwerror.h" +#include "gwfield.h" +#include "response.h" + +#include "pollsearchresultstask.h" + +#include "searchusertask.h" + +// the delay we allow the server to initially do the search +#define GW_POLL_INITIAL_DELAY 1000 +// the maximum number of times to poll the server +#define GW_POLL_MAXIMUM 5 +// the frequency between subsequent polls +#define GW_POLL_FREQUENCY_MS 8000 + +using namespace GroupWise; + +SearchUserTask::SearchUserTask(Task* parent): RequestTask(parent), m_polls( 0 ) +{ +} + + +SearchUserTask::~SearchUserTask() +{ +} + +void SearchUserTask::search( const QValueList<UserSearchQueryTerm> & query ) +{ + m_queryHandle = QString::number( QDateTime::currentDateTime().toTime_t () ); + Field::FieldList lst; + if ( query.isEmpty() ) + { + setError( 1, "no query terms" ); + return; + } + // object Id identifies the search for later reference + lst.append( new Field::SingleField( NM_A_SZ_OBJECT_ID, 0, NMFIELD_TYPE_UTF8, m_queryHandle ) ); + QValueList<UserSearchQueryTerm>::ConstIterator it = query.begin(); + const QValueList<UserSearchQueryTerm>::ConstIterator end = query.end(); + for ( ; it != end; ++it ) + { + Field::SingleField * fld = new Field::SingleField( (*it).field.ascii(), (*it).operation, 0, NMFIELD_TYPE_UTF8, (*it).argument ); + lst.append( fld ); + } + //lst.append( new Field::SingleField( "Given Name", 0, NMFIELD_TYPE_UTF8, [ NMFIELD_METHOD_EQUAL | NMFIELD_METHOD_MATCHBEGIN | NMFIELD_METHOD_MATCHEND | NMFIELD_METHOD_SEARCH ], searchTerm ); + // Or "Surname", NM_A_SZ_USERID, NM_A_SZ_TITLE, NM_A_SZ_DEPARTMENT in other fields + + createTransfer( "createsearch", lst ); +} + +bool SearchUserTask::take( Transfer * transfer ) +{ + if ( !forMe( transfer ) ) + return false; + Response * response = dynamic_cast<Response *>( transfer ); + if ( !response ) + return false; + if ( response->resultCode() ) + { + kdDebug( GROUPWISE_DEBUG_GLOBAL ) << k_funcinfo << "got return code in response << " << response->resultCode() << endl; + setError( response->resultCode() ); + return true; + } + // now start the results poll timer + QTimer::singleShot( GW_POLL_INITIAL_DELAY, this, SLOT( slotPollForResults() ) ); + return true; +} + +void SearchUserTask::slotPollForResults() +{ + //create a PollSearchResultsTask + PollSearchResultsTask * psrt = new PollSearchResultsTask( client()->rootTask() ); + psrt->poll( m_queryHandle ); + connect( psrt, SIGNAL( finished() ), SLOT( slotGotPollResults() ) ); + psrt->go( true ); +} + +void SearchUserTask::slotGotPollResults() +{ + PollSearchResultsTask * psrt = (PollSearchResultsTask *)sender(); + kdDebug( GROUPWISE_DEBUG_GLOBAL ) << k_funcinfo << "status code is " << psrt->queryStatus() << endl; + m_polls++; + switch ( psrt->queryStatus() ) + { + case PollSearchResultsTask::Pending: + case PollSearchResultsTask::InProgess: + if ( m_polls < GW_POLL_MAXIMUM ) // restart timer + QTimer::singleShot( GW_POLL_FREQUENCY_MS, this, SLOT( slotPollForResults() ) ); + else + setSuccess( psrt->statusCode() ); + break; + case PollSearchResultsTask::Completed: + m_results = psrt->results(); + setSuccess(); + break; + case PollSearchResultsTask::Cancelled: + setError(psrt->statusCode() ); + break; + case PollSearchResultsTask::Error: + setError( psrt->statusCode() ); + break; + case PollSearchResultsTask::TimeOut: + setError( psrt->statusCode() ); + break; + } +} + +QValueList< GroupWise::ContactDetails > SearchUserTask::results() +{ + return m_results; +} + +#include "searchusertask.moc" diff --git a/kopete/protocols/groupwise/libgroupwise/tasks/searchusertask.h b/kopete/protocols/groupwise/libgroupwise/tasks/searchusertask.h new file mode 100644 index 00000000..28c09b02 --- /dev/null +++ b/kopete/protocols/groupwise/libgroupwise/tasks/searchusertask.h @@ -0,0 +1,63 @@ +/* + Kopete Groupwise Protocol + searchusertask.h - high level search for users on the server - spawns PollSearchResultsTasks + + Copyright (c) 2005 SUSE Linux Products GmbH http://www.suse.com + + Based on Iris, Copyright (C) 2003 Justin Karneges + + Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#ifndef SEARCHUSERTASK_H +#define SEARCHUSERTASK_H + +#include "requesttask.h" + +class QTimer; + +/** +This Task performs user searching on the server + +@author SUSE AG +*/ +class SearchUserTask : public RequestTask +{ +Q_OBJECT +public: + SearchUserTask(Task* parent); + + ~SearchUserTask(); + /** + * Create the search query + * @param query a list of search terms + */ + void search( const QValueList<GroupWise::UserSearchQueryTerm> & query); + /** + * If the query was accepted, start a timer to poll for results using PollSearchResultsTask + */ + virtual bool take( Transfer * transfer ); + /** + * Access the results of the search + */ + QValueList< GroupWise::ContactDetails > results(); +protected slots: + void slotPollForResults(); + void slotGotPollResults(); +private: + QString m_queryHandle; // used to identify our query to the server, so we can poll for its results + QTimer * m_resultsPollTimer; + QValueList< GroupWise::ContactDetails > m_results; + int m_polls; +}; + +#endif diff --git a/kopete/protocols/groupwise/libgroupwise/tasks/sendinvitetask.cpp b/kopete/protocols/groupwise/libgroupwise/tasks/sendinvitetask.cpp new file mode 100644 index 00000000..b3a9614f --- /dev/null +++ b/kopete/protocols/groupwise/libgroupwise/tasks/sendinvitetask.cpp @@ -0,0 +1,42 @@ +/* + Kopete Groupwise Protocol + sendinvitetask.cpp - invites someone to join a conference + + Copyright (c) 2004 SUSE Linux AG http://www.suse.com + + Based on Iris, Copyright (C) 2003 Justin Karneges + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#include "sendinvitetask.h" + +SendInviteTask::SendInviteTask(Task* parent): RequestTask(parent) +{ +} + +SendInviteTask::~SendInviteTask() +{ +} + +void SendInviteTask::invite( const GroupWise::ConferenceGuid & guid, const QStringList & invitees, const GroupWise::OutgoingMessage & msg) +{ + Field::FieldList lst, tmp; + tmp.append( new Field::SingleField( NM_A_SZ_OBJECT_ID, 0, NMFIELD_TYPE_UTF8, guid ) ); + lst.append( new Field::MultiField( NM_A_FA_CONVERSATION, NMFIELD_METHOD_VALID, 0, NMFIELD_TYPE_ARRAY, tmp ) ); + QValueListConstIterator<QString> end = invitees.end(); + for ( QValueListConstIterator<QString> it = invitees.begin(); it != end; ++it ) + lst.append( new Field::SingleField( NM_A_SZ_DN, 0, NMFIELD_TYPE_DN, *it ) ); + if ( !msg.message.isEmpty() ) + lst.append( new Field::SingleField( NM_A_SZ_MESSAGE_BODY, 0, NMFIELD_TYPE_UTF8, msg.message ) ); + createTransfer( "sendinvite", lst ); +} diff --git a/kopete/protocols/groupwise/libgroupwise/tasks/sendinvitetask.h b/kopete/protocols/groupwise/libgroupwise/tasks/sendinvitetask.h new file mode 100644 index 00000000..c8cf1d9b --- /dev/null +++ b/kopete/protocols/groupwise/libgroupwise/tasks/sendinvitetask.h @@ -0,0 +1,43 @@ +/* + Kopete Groupwise Protocol + sendinvitetask.h - invites someone to join a conference + + Copyright (c) 2004 SUSE Linux AG http://www.suse.com + + Based on Iris, Copyright (C) 2003 Justin Karneges + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#ifndef SENDINVITETASK_H +#define SENDINVITETASK_H + +#include "gwerror.h" + +#include "requesttask.h" + +/** +This sends an invitation to a conference + +@author SUSE AG +*/ +class SendInviteTask : public RequestTask +{ +public: + SendInviteTask(Task* parent); + ~SendInviteTask(); + void invite( const GroupWise::ConferenceGuid & guid, const QStringList & invitees, const GroupWise::OutgoingMessage & msg ); +private: + QString m_confId; +}; + +#endif diff --git a/kopete/protocols/groupwise/libgroupwise/tasks/sendmessagetask.cpp b/kopete/protocols/groupwise/libgroupwise/tasks/sendmessagetask.cpp new file mode 100644 index 00000000..290b9d9b --- /dev/null +++ b/kopete/protocols/groupwise/libgroupwise/tasks/sendmessagetask.cpp @@ -0,0 +1,51 @@ +/* + Kopete Groupwise Protocol + sendmessagetask.cpp - sends a message to a conference + + Copyright (c) 2004 SUSE Linux AG http://www.suse.com + + Based on Iris, Copyright (C) 2003 Justin Karneges + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#include "sendmessagetask.h" + +SendMessageTask::SendMessageTask(Task* parent): RequestTask(parent) +{ +} + + +SendMessageTask::~SendMessageTask() +{ +} + +void SendMessageTask::message( const QStringList & recipientDNList, const OutgoingMessage & msg ) +{ + // Assumes the conference is instantiated, unlike Gaim's nm_send_message + Field::FieldList lst, tmp, msgBodies; + // list containing GUID + tmp.append( new Field::SingleField( NM_A_SZ_OBJECT_ID, 0, NMFIELD_TYPE_UTF8, msg.guid ) ); + lst.append( new Field::MultiField( NM_A_FA_CONVERSATION, NMFIELD_METHOD_VALID, 0, NMFIELD_TYPE_ARRAY, tmp ) ); + msgBodies.append( new Field::SingleField( NM_A_SZ_MESSAGE_BODY, 0, NMFIELD_TYPE_UTF8, msg.rtfMessage ) ); + // message body type indicator / separator? + msgBodies.append( new Field::SingleField( NM_A_UD_MESSAGE_TYPE, 0, NMFIELD_TYPE_UDWORD, 0 ) ); + // message body plaintext + msgBodies.append( new Field::SingleField( NM_A_SZ_MESSAGE_TEXT, 0, NMFIELD_TYPE_UTF8, msg.message ) ); + // list containing message bodies + lst.append( new Field::MultiField( NM_A_FA_MESSAGE, NMFIELD_METHOD_VALID, 0, NMFIELD_TYPE_ARRAY, msgBodies ) ); + // series of participants (may be empty ) + QValueListConstIterator<QString> end = recipientDNList.end(); + for ( QValueListConstIterator<QString> it = recipientDNList.begin(); it != end; ++it ) + lst.append( new Field::SingleField( NM_A_SZ_DN, 0, NMFIELD_TYPE_DN, *it ) ); + createTransfer( "sendmessage", lst ); +} diff --git a/kopete/protocols/groupwise/libgroupwise/tasks/sendmessagetask.h b/kopete/protocols/groupwise/libgroupwise/tasks/sendmessagetask.h new file mode 100644 index 00000000..f45e491f --- /dev/null +++ b/kopete/protocols/groupwise/libgroupwise/tasks/sendmessagetask.h @@ -0,0 +1,41 @@ +/* + Kopete Groupwise Protocol + sendmessagetask.h - sends a message to a conference + + Copyright (c) 2004 SUSE Linux AG http://www.suse.com + + Based on Iris, Copyright (C) 2003 Justin Karneges + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#ifndef SENDMESSAGETASK_H +#define SENDMESSAGETASK_H + +#include "client.h" +#include "requesttask.h" + +/** +Sends messages to a particular conference on the server + +@author SUSE AG +*/ +class SendMessageTask : public RequestTask +{ +public: + SendMessageTask(Task* parent); + ~SendMessageTask(); + + void message( const QStringList & recipientDNList, const OutgoingMessage & msg ); +}; + +#endif diff --git a/kopete/protocols/groupwise/libgroupwise/tasks/setstatustask.cpp b/kopete/protocols/groupwise/libgroupwise/tasks/setstatustask.cpp new file mode 100644 index 00000000..0744ff8a --- /dev/null +++ b/kopete/protocols/groupwise/libgroupwise/tasks/setstatustask.cpp @@ -0,0 +1,69 @@ +/* + Kopete Groupwise Protocol + setstatustask.cpp - Sets our status on the server + + Copyright (c) 2004 SUSE Linux AG http://www.suse.com + + Based on Iris, Copyright (C) 2003 Justin Karneges + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#include "setstatustask.h" + +using namespace GroupWise; + +SetStatusTask::SetStatusTask(Task* parent): RequestTask(parent) +{ +} + +SetStatusTask::~SetStatusTask() +{ +} + +void SetStatusTask::status( Status newStatus, const QString &awayMessage, const QString &autoReply ) +{ + if ( newStatus > GroupWise::Invalid ) + { + setError( 1, "Invalid Status" ); + return; + } + + m_status = newStatus; + m_awayMessage = awayMessage; + m_autoReply = autoReply; + + Field::FieldList lst; + lst.append( new Field::SingleField( NM_A_SZ_STATUS, 0, NMFIELD_TYPE_UTF8, QString::number( newStatus ) ) ); + if ( !awayMessage.isNull() ) + lst.append( new Field::SingleField( NM_A_SZ_STATUS_TEXT, 0, NMFIELD_TYPE_UTF8, awayMessage ) ); + if ( !autoReply.isNull() ) + lst.append( new Field::SingleField( NM_A_SZ_MESSAGE_BODY, 0, NMFIELD_TYPE_UTF8, autoReply ) ); + createTransfer( "setstatus", lst ); +} + +Status SetStatusTask::requestedStatus() const +{ + return m_status; +} + +QString SetStatusTask::awayMessage() const +{ + return m_awayMessage; +} + +QString SetStatusTask::autoReply() const +{ + return m_autoReply; +} + +#include "setstatustask.moc" diff --git a/kopete/protocols/groupwise/libgroupwise/tasks/setstatustask.h b/kopete/protocols/groupwise/libgroupwise/tasks/setstatustask.h new file mode 100644 index 00000000..2d3c53d7 --- /dev/null +++ b/kopete/protocols/groupwise/libgroupwise/tasks/setstatustask.h @@ -0,0 +1,46 @@ +/* + Kopete Groupwise Protocol + setstatustask.h - Sets our status on the server + + Copyright (c) 2004 SUSE Linux AG http://www.suse.com + + Based on Iris, Copyright (C) 2003 Justin Karneges + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#ifndef SETSTATUSTASK_H +#define SETSTATUSTASK_H + +#include "gwerror.h" +#include "requesttask.h" + +/** +@author Kopete Developers +*/ +class SetStatusTask : public RequestTask +{ +Q_OBJECT +public: + SetStatusTask(Task* parent); + ~SetStatusTask(); + void status( GroupWise::Status newStatus, const QString &awayMessage, const QString &autoReply ); + GroupWise::Status requestedStatus() const; + QString awayMessage() const; + QString autoReply() const; +private: + GroupWise::Status m_status; + QString m_awayMessage; + QString m_autoReply; +}; + +#endif diff --git a/kopete/protocols/groupwise/libgroupwise/tasks/statustask.cpp b/kopete/protocols/groupwise/libgroupwise/tasks/statustask.cpp new file mode 100644 index 00000000..8f8eccd4 --- /dev/null +++ b/kopete/protocols/groupwise/libgroupwise/tasks/statustask.cpp @@ -0,0 +1,47 @@ +/* + Kopete Groupwise Protocol + statustask.cpp - Event handling task responsible for status change events + + Copyright (c) 2004 SUSE Linux AG http://www.suse.com + + Based on Iris, Copyright (C) 2003 Justin Karneges + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#include "client.h" + +#include "statustask.h" + +StatusTask::StatusTask(Task* parent): EventTask(parent) +{ + registerEvent( GroupWise::StatusChange ); +} + +StatusTask::~StatusTask() +{ +} + +bool StatusTask::take( Transfer * transfer ) +{ + EventTransfer * event; + if ( forMe( transfer, event ) ) + { + client()->debug( "Got a status change!" ); + client()->debug( QString( "%1 changed status to %2, message: %3" ).arg( event->source() ).arg( event->status() ).arg( event->statusText() ) ); + emit gotStatus( event->source().lower(), event->status(), event->statusText() ); + return true; + } + else + return false; +} +#include "statustask.moc" diff --git a/kopete/protocols/groupwise/libgroupwise/tasks/statustask.h b/kopete/protocols/groupwise/libgroupwise/tasks/statustask.h new file mode 100644 index 00000000..8e4994ff --- /dev/null +++ b/kopete/protocols/groupwise/libgroupwise/tasks/statustask.h @@ -0,0 +1,40 @@ +/* + Kopete Groupwise Protocol + statustask.h - Event handling task responsible for status change events + + Copyright (c) 2004 SUSE Linux AG http://www.suse.com + + Based on Iris, Copyright (C) 2003 Justin Karneges + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#ifndef STATUSTASK_H +#define STATUSTASK_H + +#include "eventtask.h" + +/** +@author Kopete Developers +*/ +class StatusTask : public EventTask +{ +Q_OBJECT +public: + StatusTask(Task* parent); + ~StatusTask(); + bool take( Transfer * transfer ); +signals: + void gotStatus( const QString & contactId, Q_UINT16 status, const QString & statusText ); +}; + +#endif diff --git a/kopete/protocols/groupwise/libgroupwise/tasks/tests/Makefile.am b/kopete/protocols/groupwise/libgroupwise/tasks/tests/Makefile.am new file mode 100644 index 00000000..6a10925b --- /dev/null +++ b/kopete/protocols/groupwise/libgroupwise/tasks/tests/Makefile.am @@ -0,0 +1,7 @@ +INCLUDES = -I$(top_srcdir)/protocols/groupwise/libgroupwise/qca/src -I$(srcdir)/../../libgroupwise/ -I$(top_srcdir)/kopete/protocols/groupwise/libgroupwise/qca/src $(all_includes) +METASOURCES = AUTO +noinst_PROGRAMS = task_take_test + +task_take_test_LDADD = -lqt-mt ../../libgwtest.la + +task_take_test_SOURCES = task_take_test.cpp diff --git a/kopete/protocols/groupwise/libgroupwise/tasks/tests/task_take_test.cpp b/kopete/protocols/groupwise/libgroupwise/tasks/tests/task_take_test.cpp new file mode 100644 index 00000000..140e851f --- /dev/null +++ b/kopete/protocols/groupwise/libgroupwise/tasks/tests/task_take_test.cpp @@ -0,0 +1,18 @@ +// +// C++ Implementation: task_take_test +// +// Description: +// +// +// Author: Kopete Developers <kopete-devel@kde.org>, (C) 2004 +// +// Copyright: See COPYING file that comes with this distribution +// +// + +//#include "requesttask.h" + +int main() +{ + // balls, root task requires client, will test in situ instead +} diff --git a/kopete/protocols/groupwise/libgroupwise/tasks/typingtask.cpp b/kopete/protocols/groupwise/libgroupwise/tasks/typingtask.cpp new file mode 100644 index 00000000..b835c525 --- /dev/null +++ b/kopete/protocols/groupwise/libgroupwise/tasks/typingtask.cpp @@ -0,0 +1,44 @@ +/* + Kopete Groupwise Protocol + typingtask.cpp - sends typing notifications to the server + + Copyright (c) 2004 SUSE Linux AG http://www.suse.com + + Based on Iris, Copyright (C) 2003 Justin Karneges + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +//#include "eventtransfer.h" + +#include "typingtask.h" + +TypingTask::TypingTask(Task* parent): RequestTask(parent) +{ +} + + +TypingTask::~TypingTask() +{ +} + +void TypingTask::typing( const GroupWise::ConferenceGuid & conferenceGuid, const bool typing ) +{ + Field::FieldList typingNotification, outgoingList; + typingNotification.append( new Field::SingleField( NM_A_SZ_OBJECT_ID, 0, NMFIELD_TYPE_UTF8, conferenceGuid ) ); + typingNotification.append( new Field::SingleField( NM_A_SZ_TYPE, 0, NMFIELD_TYPE_UTF8, + QString::number( typing ? GroupWise::UserTyping : GroupWise::UserNotTyping ) ) ); + outgoingList.append( new Field::MultiField( NM_A_FA_CONVERSATION, NMFIELD_METHOD_VALID, 0, NMFIELD_TYPE_ARRAY, typingNotification ) ); + createTransfer( "sendtyping", outgoingList ); +} + +#include "typingtask.moc" diff --git a/kopete/protocols/groupwise/libgroupwise/tasks/typingtask.h b/kopete/protocols/groupwise/libgroupwise/tasks/typingtask.h new file mode 100644 index 00000000..4f4da1cd --- /dev/null +++ b/kopete/protocols/groupwise/libgroupwise/tasks/typingtask.h @@ -0,0 +1,41 @@ +/* + Kopete Groupwise Protocol + typingtask.h - sends typing notifications to the server + + Copyright (c) 2004 SUSE Linux AG http://www.suse.com + + Based on Iris, Copyright (C) 2003 Justin Karneges + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#ifndef TYPINGTASK_H +#define TYPINGTASK_H + +#include "requesttask.h" + +/** + Notifies the server that we are typing or are no longer typing in a particular conversation + +@author Kopete Developers +*/ +class TypingTask : public RequestTask +{ +Q_OBJECT + +public: + TypingTask(Task* parent); + ~TypingTask(); + void typing( const GroupWise::ConferenceGuid & guid, const bool typing ); +}; + +#endif diff --git a/kopete/protocols/groupwise/libgroupwise/tasks/updatecontacttask.cpp b/kopete/protocols/groupwise/libgroupwise/tasks/updatecontacttask.cpp new file mode 100644 index 00000000..d8c1a68a --- /dev/null +++ b/kopete/protocols/groupwise/libgroupwise/tasks/updatecontacttask.cpp @@ -0,0 +1,76 @@ +/* + Kopete Groupwise Protocol + updatecontacttask.cpp - rename a contact on the server + + Copyright (c) 2004 SUSE Linux AG http://www.suse.com + + Based on Iris, Copyright (C) 2003 Justin Karneges + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#include "gwfield.h" + +#include "updatecontacttask.h" + +using namespace GroupWise; + +UpdateContactTask::UpdateContactTask(Task* parent): UpdateItemTask(parent) +{ +} + + +UpdateContactTask::~UpdateContactTask() +{ +} + +QString UpdateContactTask::displayName() +{ + return m_name; +} + +void UpdateContactTask::renameContact( const QString & newName, const QValueList<ContactItem> & contactInstances ) +{ + m_name = newName; + // build a list of delete, add fields that removes each instance on the server and then readds it with the new name + Field::FieldList lst; + const QValueList<ContactItem>::ConstIterator end = contactInstances.end(); + for( QValueList<ContactItem>::ConstIterator it = contactInstances.begin(); it != end; ++it ) + { + Field::FieldList contactFields; + contactFields.append( new Field::SingleField( NM_A_SZ_OBJECT_ID, 0, NMFIELD_TYPE_UTF8, (*it).id ) ); + contactFields.append( new Field::SingleField( NM_A_SZ_PARENT_ID, 0, NMFIELD_TYPE_UTF8, (*it).parentId ) ); + contactFields.append( new Field::SingleField( NM_A_SZ_SEQUENCE_NUMBER, 0, NMFIELD_TYPE_UTF8, (*it).sequence ) ); + if ( !(*it).dn.isNull() ) + contactFields.append( new Field::SingleField( NM_A_SZ_DN, 0, NMFIELD_TYPE_UTF8, (*it).dn ) ); + if ( !(*it).displayName.isNull() ) + contactFields.append( new Field::SingleField( NM_A_SZ_DISPLAY_NAME, 0, NMFIELD_TYPE_UTF8, (*it).displayName ) ); + lst.append( + new Field::MultiField( NM_A_FA_CONTACT, NMFIELD_METHOD_DELETE, 0, NMFIELD_TYPE_ARRAY, contactFields ) ); + } + for( QValueList<ContactItem>::ConstIterator it = contactInstances.begin(); it != end; ++it ) + { + Field::FieldList contactFields; + contactFields.append( new Field::SingleField( NM_A_SZ_OBJECT_ID, 0, NMFIELD_TYPE_UTF8, (*it).id ) ); + contactFields.append( new Field::SingleField( NM_A_SZ_PARENT_ID, 0, NMFIELD_TYPE_UTF8, (*it).parentId ) ); + contactFields.append( new Field::SingleField( NM_A_SZ_SEQUENCE_NUMBER, 0, NMFIELD_TYPE_UTF8, (*it).sequence ) ); + if ( !(*it).dn.isNull() ) + contactFields.append( new Field::SingleField( NM_A_SZ_DN, 0, NMFIELD_TYPE_UTF8, (*it).dn ) ); + contactFields.append( new Field::SingleField( NM_A_SZ_DISPLAY_NAME, 0, NMFIELD_TYPE_UTF8, newName ) ); + lst.append( + new Field::MultiField( NM_A_FA_CONTACT, NMFIELD_METHOD_ADD, 0, NMFIELD_TYPE_ARRAY, contactFields ) ); + } + //lst.dump( true ); + UpdateItemTask::item( lst ); +} + +#include "updatecontacttask.moc" diff --git a/kopete/protocols/groupwise/libgroupwise/tasks/updatecontacttask.h b/kopete/protocols/groupwise/libgroupwise/tasks/updatecontacttask.h new file mode 100644 index 00000000..7e6ac899 --- /dev/null +++ b/kopete/protocols/groupwise/libgroupwise/tasks/updatecontacttask.h @@ -0,0 +1,44 @@ +/* + Kopete Groupwise Protocol + updatecontacttask.h - rename a contact on the server + + Copyright (c) 2004 SUSE Linux AG http://www.suse.com + + Based on Iris, Copyright (C) 2003 Justin Karneges + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#ifndef UPDATECONTACTTASK_H +#define UPDATECONTACTTASK_H + +#include "gwerror.h" + +#include "updateitemtask.h" + +/** + * Renames a contact on the server + * @author Kopete Developers + */ +class UpdateContactTask : public UpdateItemTask +{ +Q_OBJECT +public: + UpdateContactTask(Task* parent); + ~UpdateContactTask(); + void renameContact( const QString& newName, const QValueList<GroupWise::ContactItem> & contactInstances ); + QString displayName(); +private: + QString m_name; +}; + +#endif diff --git a/kopete/protocols/groupwise/libgroupwise/tasks/updatefoldertask.cpp b/kopete/protocols/groupwise/libgroupwise/tasks/updatefoldertask.cpp new file mode 100644 index 00000000..fef5d2fe --- /dev/null +++ b/kopete/protocols/groupwise/libgroupwise/tasks/updatefoldertask.cpp @@ -0,0 +1,59 @@ +/* + Kopete Groupwise Protocol + updatefoldertask.cpp - rename a folder on the server + + Copyright (c) 2004 SUSE Linux AG http://www.suse.com + + Based on Iris, Copyright (C) 2003 Justin Karneges + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ +#include "gwfield.h" + +#include "updatefoldertask.h" + +UpdateFolderTask::UpdateFolderTask(Task* parent): UpdateItemTask(parent) +{ +} + +UpdateFolderTask::~UpdateFolderTask() +{ +} + +void UpdateFolderTask::renameFolder( const QString & newName, const GroupWise::FolderItem & existing ) +{ + Field::FieldList lst; + // add the old version of the folder, marked delete + lst.append( new Field::MultiField( NM_A_FA_FOLDER, NMFIELD_METHOD_DELETE, 0, NMFIELD_TYPE_ARRAY, folderToFields( existing) ) ); + + GroupWise::FolderItem renamed = existing; + renamed.name = newName; + // add the new version of the folder, marked add + lst.append( new Field::MultiField( NM_A_FA_FOLDER, NMFIELD_METHOD_ADD, 0, NMFIELD_TYPE_ARRAY, folderToFields( renamed ) ) ); + // let our parent class package it up as a contactlist in a transfer + UpdateItemTask::item( lst ); +} + +Field::FieldList UpdateFolderTask::folderToFields( const GroupWise::FolderItem & folder ) +{ + Field::FieldList lst; + lst.append( new Field::SingleField( NM_A_SZ_OBJECT_ID, 0, NMFIELD_TYPE_UTF8, folder.id ) ); + lst.append( new Field::SingleField( NM_A_SZ_PARENT_ID, 0, NMFIELD_TYPE_UTF8, 0 ) ); + lst.append( new Field::SingleField( NM_A_SZ_TYPE, 0, NMFIELD_TYPE_UTF8, 1 ) ); + lst.append( new Field::SingleField( NM_A_SZ_SEQUENCE_NUMBER, 0, NMFIELD_TYPE_UTF8, folder.sequence ) ); + if ( !folder.name.isEmpty() ) + lst.append( new Field::SingleField( NM_A_SZ_DISPLAY_NAME, 0, NMFIELD_TYPE_UTF8, folder.name ) ); + return lst; +} + +#include "updatefoldertask.moc" + diff --git a/kopete/protocols/groupwise/libgroupwise/tasks/updatefoldertask.h b/kopete/protocols/groupwise/libgroupwise/tasks/updatefoldertask.h new file mode 100644 index 00000000..230bd563 --- /dev/null +++ b/kopete/protocols/groupwise/libgroupwise/tasks/updatefoldertask.h @@ -0,0 +1,42 @@ +/* + Kopete Groupwise Protocol + updatefoldertask.h - rename a folder on the server + + Copyright (c) 2004 SUSE Linux AG http://www.suse.com + + Based on Iris, Copyright (C) 2003 Justin Karneges + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#ifndef UPDATEFOLDERTASK_H +#define UPDATEFOLDERTASK_H + +#include <updateitemtask.h> + +/** +Renames a folder on the server + +@author Kopete Developers +*/ +class UpdateFolderTask : public UpdateItemTask +{ +Q_OBJECT +public: + UpdateFolderTask(Task* parent); + ~UpdateFolderTask(); + void renameFolder( const QString & newName, const GroupWise::FolderItem & existing ); +protected: + Field::FieldList folderToFields( const GroupWise::FolderItem & folder ); +}; + +#endif diff --git a/kopete/protocols/groupwise/libgroupwise/tasks/updateitemtask.cpp b/kopete/protocols/groupwise/libgroupwise/tasks/updateitemtask.cpp new file mode 100644 index 00000000..1af4ef12 --- /dev/null +++ b/kopete/protocols/groupwise/libgroupwise/tasks/updateitemtask.cpp @@ -0,0 +1,39 @@ +/* + Kopete Groupwise Protocol + updateitemtask.cpp - ancestor for tasks that rename objects on the server + + Copyright (c) 2004 SUSE Linux AG http://www.suse.com + + Based on Iris, Copyright (C) 2003 Justin Karneges + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#include "updateitemtask.h" + +UpdateItemTask::UpdateItemTask( Task* parent) : RequestTask( parent ) +{ +} + + +UpdateItemTask::~UpdateItemTask() +{ +} + +void UpdateItemTask::item( Field::FieldList updateItemFields ) +{ + Field::FieldList lst; + lst.append( new Field::MultiField( NM_A_FA_CONTACT_LIST, NMFIELD_METHOD_VALID, 0, NMFIELD_TYPE_ARRAY, updateItemFields ) ); + createTransfer( "updateitem", lst ); +} + +#include "updateitemtask.moc" diff --git a/kopete/protocols/groupwise/libgroupwise/tasks/updateitemtask.h b/kopete/protocols/groupwise/libgroupwise/tasks/updateitemtask.h new file mode 100644 index 00000000..a087d276 --- /dev/null +++ b/kopete/protocols/groupwise/libgroupwise/tasks/updateitemtask.h @@ -0,0 +1,40 @@ +/* + Kopete Groupwise Protocol + updateitemtask.h - ancestor for tasks that rename objects on the server + + Copyright (c) 2004 SUSE Linux AG http://www.suse.com + + Based on Iris, Copyright (C) 2003 Justin Karneges + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#ifndef UPDATEITEMTASK_H +#define UPDATEITEMTASK_H + +#include "requesttask.h" + +/** +Rename a folder or contact on the server. In future may be used for changing the order of folders or contacts relative to one another, but this is not supported by Kopete yet. + +@author SUSE AG +*/ +class UpdateItemTask : public RequestTask +{ +Q_OBJECT +public: + UpdateItemTask( Task* parent ); + ~UpdateItemTask(); + void item( Field::FieldList updateItemFields ); +}; + +#endif diff --git a/kopete/protocols/groupwise/libgroupwise/tests/Makefile.am b/kopete/protocols/groupwise/libgroupwise/tests/Makefile.am new file mode 100644 index 00000000..33a603ad --- /dev/null +++ b/kopete/protocols/groupwise/libgroupwise/tests/Makefile.am @@ -0,0 +1,22 @@ +INCLUDES = -I$(top_srcdir)/kopete/protocols/groupwise/libgroupwise/qca/src \ + -I$(top_srcdir)/kopete/protocols/groupwise/libgroupwise \ + -I$(top_srcdir)/kopete/protocols/groupwise \ + $(all_includes) +METASOURCES = AUTO +noinst_PROGRAMS = clientstream_test field_test coreprotocol_test client_test +coreprotocol_test_LDFLAGS = -no-undefined $(all_libraries) +coreprotocol_test_SOURCES = coreprotocol_test.cpp +coreprotocol_test_LDADD = \ + ../libgwtest.la -lqt-mt +field_test_LDFLAGS = -no-undefined $(all_libraries) +field_test_SOURCES = field_test.cpp +field_test_LDADD = \ + ../libgwtest.la -lqt-mt + +clientstream_test_SOURCES = clientstream_test.cpp +clientstream_test_LDADD = -lqt-mt \ + ../../kopete_groupwise.la + +client_test_SOURCES = client_test.cpp +client_test_LDADD = ../../../../protocols/groupwise/kopete_groupwise.la \ + ../libgwtest.la -lqt-mt diff --git a/kopete/protocols/groupwise/libgroupwise/tests/client_test.cpp b/kopete/protocols/groupwise/libgroupwise/tests/client_test.cpp new file mode 100644 index 00000000..22f92282 --- /dev/null +++ b/kopete/protocols/groupwise/libgroupwise/tests/client_test.cpp @@ -0,0 +1,10 @@ +#include "client.h" +#include "task.h" + +int main() +{ + Client c; + Task rootTask( &c, true ); + + return 0; +} diff --git a/kopete/protocols/groupwise/libgroupwise/tests/clientstream_test.cpp b/kopete/protocols/groupwise/libgroupwise/tests/clientstream_test.cpp new file mode 100644 index 00000000..bbd10ee8 --- /dev/null +++ b/kopete/protocols/groupwise/libgroupwise/tests/clientstream_test.cpp @@ -0,0 +1,107 @@ +#include "clientstream_test.h" + +ClientStreamTest::ClientStreamTest(int argc, char ** argv) : QApplication( argc, argv ) +{ + // set up client stream + myConnector = new KNetworkConnector( 0 ); + //myConnector->setOptHostPort( "localhost", 8300 ); + myConnector->setOptHostPort( "reiser.suse.de", 8300 ); + myConnector->setOptSSL( true ); + Q_ASSERT( QCA::isSupported(QCA::CAP_TLS) ); + myTLS = new QCA::TLS; + myTLSHandler = new QCATLSHandler( myTLS ); + myTestObject = new ClientStream( myConnector, myTLSHandler, 0); + // notify when the transport layer is connected + connect( myTestObject, SIGNAL( connected() ), SLOT( slotConnected() ) ); + // it's necessary to catch this signal and tell the TLS handler to proceed, even if we don't check cert validity + connect( myTLSHandler, SIGNAL(tlsHandshaken()), SLOT(slotTLSHandshaken()) ); + // notify and start sending + connect( myTestObject, SIGNAL( securityLayerActivated(int) ), SLOT( slotSend(int) ) ); + connect( myTestObject, SIGNAL( warning(int) ), SLOT( slotWarning(int) ) ); + + // do test once the event loop is running + QTimer::singleShot( 0, this, SLOT( slotDoTest() ) ); +} + +ClientStreamTest::~ClientStreamTest() +{ + delete myTestObject; + delete myTLSHandler; + delete myTLS; + delete myConnector; +} + +void ClientStreamTest::slotDoTest() +{ + NovellDN dn; + dn.dn = "maeuschen"; + dn.server = "reiser.suse.de"; + // connect to server + qDebug( "connecting to server "); + myTestObject->connectToServer( dn, true ); // fine up to here... +} + +void ClientStreamTest::slotConnected() +{ + qDebug( "connection is up"); +} + +void ClientStreamTest::slotWarning(int warning) +{ + qDebug( "warning: %i", warning); +} + +void ClientStreamTest::slotsend(int layer) +{ + qDebug( "security layer is up: %i", layer); + RequestFactory testFactory; + // we're not connecting... + qDebug( "sending request" ); + // send a request + QCString command("login"); + Request * firstRequest = testFactory.request( command ); + Field::FieldList lst; + lst.append( new Field::SingleField( NM_A_SZ_USERID, 0, NMFIELD_TYPE_UTF8, "maeuschen" ) ); + lst.append( new Field::SingleField( NM_A_SZ_CREDENTIALS, 0, NMFIELD_TYPE_UTF8, "maeuschen" ) ); + lst.append( new Field::SingleField( NM_A_SZ_USER_AGENT, 0, NMFIELD_TYPE_UTF8, "libgroupwise/0.1 (linux, 2.6.5-7.97-smp)" ) ); + lst.append( new Field::SingleField( NM_A_UD_BUILD, 0, NMFIELD_TYPE_UDWORD, 2 ) ); + lst.append( new Field::SingleField( NM_A_IP_ADDRESS, 0, NMFIELD_TYPE_UTF8, "10.10.11.103" ) ); + firstRequest->setFields( lst ); + myTestObject->write( firstRequest ); + qDebug( "done"); +} + +void ClientStreamTest::slotTLSHandshaken() +{ + qDebug( "TLS handshake complete" ); + int validityResult = myTLS->certificateValidityResult (); + + if( validityResult == QCA::TLS::Valid ) + { + qDebug( "Certificate is valid, continuing."); + // valid certificate, continue + myTLSHandler->continueAfterHandshake (); + } + else + { + qDebug( "Certificate is not valid, continuing" ); + // certificate is not valid, query the user + /* if(handleTLSWarning (validityResult, server (), myself()->contactId ()) == KMessageBox::Continue) + {*/ + myTLSHandler->continueAfterHandshake (); + /* } + else + { + disconnect ( Kopete::Account::Manual ); + }*/ + } + +} +int main(int argc, char ** argv) +{ + ClientStreamTest a( argc, argv ); + a.exec(); + return 0; +} + +#include "clientstream_test.moc" diff --git a/kopete/protocols/groupwise/libgroupwise/tests/clientstream_test.h b/kopete/protocols/groupwise/libgroupwise/tests/clientstream_test.h new file mode 100644 index 00000000..2c77f4e1 --- /dev/null +++ b/kopete/protocols/groupwise/libgroupwise/tests/clientstream_test.h @@ -0,0 +1,57 @@ +// +// C++ Implementation: clientstream_test +// +// Description: +// +// +// Author: Kopete Developers <kopete-devel@kde.org>, (C) 2004 +// +// Copyright: See COPYING file that comes with this distribution +// +// + +#ifndef clientstream_test_h +#define clientstream_test_h + +#include <qglobal.h> +#include <qapplication.h> +#include <qtimer.h> + +#include "gwclientstream.h" +#include "gwconnector.h" +#include <qca.h> +#include "qcatlshandler.h" +#include "requestfactory.h" +#include "request.h" +#include "usertransfer.h" + +#include "coreprotocol.h" + +#define QT_FATAL_ASSERT 1 + +class ClientStreamTest : public QApplication +{ +Q_OBJECT +public: + ClientStreamTest(int argc, char ** argv); + + ~ClientStreamTest(); + +public slots: + void slotDoTest(); + + void slotConnected(); + + void slotWarning(int warning); + + void slotsend(int layer); + void slotTLSHandshaken(); + +private: + KNetworkConnector *myConnector; + QCA::TLS *myTLS; + QCATLSHandler *myTLSHandler; + ClientStream *myTestObject; +}; + +#endif diff --git a/kopete/protocols/groupwise/libgroupwise/tests/coreprotocol_test.cpp b/kopete/protocols/groupwise/libgroupwise/tests/coreprotocol_test.cpp new file mode 100644 index 00000000..d1de6084 --- /dev/null +++ b/kopete/protocols/groupwise/libgroupwise/tests/coreprotocol_test.cpp @@ -0,0 +1,30 @@ +// +// C++ Implementation: coreprotocol_test +// +// Description: +// +// +// Author: Kopete Developers <kopete-devel@kde.org>, (C) 2004 +// +// Copyright: See COPYING file that comes with this distribution +// +// + +#include "requestfactory.h" +#include "request.h" +#include "usertransfer.h" + +#include "coreprotocol.h" + +int main() +{ + CoreProtocol testObject; + RequestFactory testFactory; + QCString command("login"); + Request * firstRequest = testFactory.request( command ); + Field::FieldList lst; + lst.append( new Field::SingleField( NM_A_SZ_USERID, NMFIELD_METHOD_VALID, 0, NMFIELD_TYPE_UTF8, "blah@fasel.org" ) ); + firstRequest->setFields( lst ); + testObject.outgoingTransfer( firstRequest ); + return 0; +} diff --git a/kopete/protocols/groupwise/libgroupwise/tests/field_test.cpp b/kopete/protocols/groupwise/libgroupwise/tests/field_test.cpp new file mode 100644 index 00000000..eec3f1f2 --- /dev/null +++ b/kopete/protocols/groupwise/libgroupwise/tests/field_test.cpp @@ -0,0 +1,154 @@ +#include "gwfield.h" +#include <stdio.h> + +static Field::FieldList fl; + +void buildList(); +void buildFakeContactList(); +void extractFields( Field::FieldList ); + +int main() +{ + buildFakeContactList(); + // look for a field in the list +/* if ( fl.find( NM_A_FA_MESSAGE ) != fl.end() ) + printf( "Found a field, where there was supposed to be one :)\n" ); + else + printf( "Didn't find a field, where there was supposed to be one :(\n" ); + Field::FieldListIterator it; + if ( (it = fl.find( NM_A_SZ_OBJECT_ID ) ) != fl.end() ) + printf( "Found a field, where there was NOT supposed to be one :(\n" ); + else + printf( "Didn't find a field, where there wasn't supposed to be one :)\n" );*/ + //printf( "%i\n", static_cast<Field::MultiField*>(*it) ); + // dump the list + fl.dump( true ); + + printf( "\nNow testing find routines.\n"); + // find the field containing the contact list + Field::MultiField * clf = dynamic_cast< Field::MultiField * >( *(fl.find( NM_A_FA_CONTACT_LIST ) ) ); + if ( clf ) + { + Field::FieldList cl = clf->fields(); + // look for a folder in the list + Field::FieldListIterator it = cl.find( NM_A_FA_FOLDER ); + if ( it != cl.end() ) + printf( "Found the first folder :)\n"); + else + printf( "Didn't find the first folder, where did it go? :(\n"); + + printf( "Looking for a second folder :)\n"); + it = cl.find( ++it, NM_A_FA_FOLDER ); + if ( it == cl.end() ) + printf( "Didn't find a second folder :)\n" ); + else + printf( "Found a second folder, now did that get there? :(\n"); + } + else + printf( "Didn't find the contact list, where did it go? :(\n"); + + //extractFields( fl ); + return 0; +} +// test Field subclasses by creating various FieldLists and recovering the data + +void buildList() +{ + // STRUCTURE + // fl - top list + // sf - faust quote + // mf - Multifield - participants, containing + // nl - nested list + // sf - contactlist (empty field array) + // sf - message body + + Field::SingleField* sf = new Field::SingleField( NM_A_FA_MESSAGE, 0, NMFIELD_TYPE_UTF8, QString::fromLatin1( "Da steh ich nun, ich armer Tor! Und bin so klug als wie zuvor..." ) ); + fl.append( sf ); + sf = new Field::SingleField( NM_A_SZ_TRANSACTION_ID, 0, NMFIELD_TYPE_UTF8, QString::fromLatin1( "maeuschen" ) ); + fl.append( sf ); + // nested list + Field::FieldList nl; + sf = new Field::SingleField( NM_A_SZ_STATUS, 0, NMFIELD_TYPE_UDWORD, 123 ); + nl.append( sf ); + sf = new Field::SingleField( NM_A_SZ_MESSAGE_BODY, 0, NMFIELD_TYPE_UTF8, QString::fromLatin1( "bla bla" ) ); + nl.append( sf ); + Field::MultiField* mf = new Field::MultiField( NM_A_FA_PARTICIPANTS, NMFIELD_METHOD_VALID, 0, NMFIELD_TYPE_ARRAY ); + mf->setFields( nl ); + fl.append( mf ); + +/* Field::SingleField * ext = sf; + printf( "tag: %s flags: %i type: %i value: %s\n", ext->tag().data(), ext->flags(), ext->type(), ext->value().toString().ascii() );*/ +} + +void buildFakeContactList() +{ + using namespace Field; + + FieldList contactlist; + // add a few contacts + { + const char* names[] = { "apple", "banana", "cherry", "damson", "elderberry", "framboise" }; + for ( int i = 0; i < 6; i ++ ) + { + FieldList contact; + Field::SingleField* sf = new Field::SingleField( NM_A_SZ_OBJECT_ID, 0, NMFIELD_TYPE_UTF8, QString::number( i ) ); + contact.append( sf ); + sf = new Field::SingleField( NM_A_SZ_DISPLAY_NAME, 0, NMFIELD_TYPE_UTF8, names[i] ); + contact.append( sf ); + MultiField* mf = new MultiField( NM_A_FA_CONTACT, NMFIELD_METHOD_VALID, 0, NMFIELD_TYPE_ARRAY, contact ); + contactlist.append( mf ); + } + } + // add a folder + { + FieldList folder; + Field::SingleField* sf = new Field::SingleField( NM_A_SZ_OBJECT_ID, 0, NMFIELD_TYPE_UTF8, QString::number( 1 ) ); + folder.append( sf ); + sf = new Field::SingleField( NM_A_SZ_DISPLAY_NAME, 0, NMFIELD_TYPE_UTF8, "buddies" ); + folder.append( sf ); + MultiField* mf = new MultiField( NM_A_FA_FOLDER, NMFIELD_METHOD_VALID, 0, NMFIELD_TYPE_ARRAY, folder ); + contactlist.append( mf ); + } + // add some more contacts + { + const char* names[] = { "aardvark", "boar", "cat" }; + for ( int i = 0; i < 3; i ++ ) + { + FieldList contact; + Field::SingleField* sf = new Field::SingleField( NM_A_SZ_OBJECT_ID, 0, NMFIELD_TYPE_UTF8, QString::number( i ) ); + contact.append( sf ); + sf = new Field::SingleField( NM_A_SZ_DISPLAY_NAME, 0, NMFIELD_TYPE_UTF8, names[i] ); + contact.append( sf ); + MultiField* mf = new MultiField( NM_A_FA_CONTACT, NMFIELD_METHOD_VALID, 0, NMFIELD_TYPE_ARRAY, contact ); + contactlist.append( mf ); + } + } + + + MultiField * cl = new MultiField( NM_A_FA_CONTACT_LIST, NMFIELD_METHOD_VALID, 0, NMFIELD_TYPE_ARRAY, contactlist ); + fl.append( cl ); +} + +void extractFields( Field::FieldList l ) +{ + Field::FieldListIterator it; + printf ("iterating over %i fields\n", l.count() ); + for ( it = l.begin(); it != l.end() ; ++it ) + { + printf ("field\n"); + Field::SingleField * ext = dynamic_cast<Field::SingleField *>( *it ); + if ( ext ) + printf( "tag: %s flags: %i type: %i value: %s\n", ext->tag().data(), ext->flags(), ext->type(), ext->value().toString().ascii() ); + else + { + Field::MultiField* mf = dynamic_cast<Field::MultiField *>( *it ); + if ( mf ) + { + printf( "found a multi value field\n" ); + extractFields( mf->fields() ); + } + } + } + + printf ("done\n"); +} diff --git a/kopete/protocols/groupwise/libgroupwise/tlshandler.cpp b/kopete/protocols/groupwise/libgroupwise/tlshandler.cpp new file mode 100644 index 00000000..290dba36 --- /dev/null +++ b/kopete/protocols/groupwise/libgroupwise/tlshandler.cpp @@ -0,0 +1,31 @@ +/* + tlshandler.cpp - Kopete Groupwise Protocol + + Copyright (c) 2004 SUSE Linux AG http://www.suse.com + + Based on Iris, Copyright (C) 2003 Justin Karneges + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#include "tlshandler.h" + +TLSHandler::TLSHandler(QObject *parent) +:QObject(parent) +{ +} + +TLSHandler::~TLSHandler() +{ +} + +#include "tlshandler.moc" diff --git a/kopete/protocols/groupwise/libgroupwise/tlshandler.h b/kopete/protocols/groupwise/libgroupwise/tlshandler.h new file mode 100644 index 00000000..61c8fe7d --- /dev/null +++ b/kopete/protocols/groupwise/libgroupwise/tlshandler.h @@ -0,0 +1,51 @@ +/* + tlshandler.h - Kopete Groupwise Protocol + + Copyright (c) 2004 SUSE Linux AG http://www.suse.com + + Based on Iris, Copyright (C) 2003 Justin Karneges + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#ifndef GWTLSHANDLER_H +#define GWTLSHANDLER_H + +#include <qobject.h> +//#include<qstring.h> +//#include<qhostaddress.h> +//#include<qstring.h> +//#include<qcstring.h> +//#include<qxml.h> +//#include<qdom.h> + +class TLSHandler : public QObject +{ + Q_OBJECT +public: + TLSHandler(QObject *parent=0); + virtual ~TLSHandler(); + + virtual void reset()=0; + virtual void startClient(const QString &host)=0; + virtual void write(const QByteArray &a)=0; + virtual void writeIncoming(const QByteArray &a)=0; + +signals: + void success(); + void fail(); + void closed(); + void readyRead(const QByteArray &a); + void readyReadOutgoing(const QByteArray &a, int plainBytes); +}; + +#endif diff --git a/kopete/protocols/groupwise/libgroupwise/transfer.cpp b/kopete/protocols/groupwise/libgroupwise/transfer.cpp new file mode 100644 index 00000000..366deed0 --- /dev/null +++ b/kopete/protocols/groupwise/libgroupwise/transfer.cpp @@ -0,0 +1,30 @@ +/* + transfer.cpp - Kopete Groupwise Protocol + + Copyright (c) 2004 SUSE Linux AG http://www.suse.com + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ +#include <qapplication.h> + +#include "transfer.h" + +Transfer::Transfer() +{ +} + + +Transfer::~Transfer() +{ +} + + diff --git a/kopete/protocols/groupwise/libgroupwise/transfer.h b/kopete/protocols/groupwise/libgroupwise/transfer.h new file mode 100644 index 00000000..b46f81ea --- /dev/null +++ b/kopete/protocols/groupwise/libgroupwise/transfer.h @@ -0,0 +1,34 @@ +/* + transfer.h - Kopete Groupwise Protocol + + Copyright (c) 2004 SUSE Linux AG http://www.suse.com + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#ifndef TRANSFER_H +#define TRANSFER_H + +/** +@author Kopete Developers +*/ +class Transfer{ +public: + enum TransferType { EventTransfer, RequestTransfer, ResponseTransfer }; + Transfer(); + virtual ~Transfer(); + + virtual TransferType type() = 0; + +}; + +#endif diff --git a/kopete/protocols/groupwise/libgroupwise/transferbase.cpp b/kopete/protocols/groupwise/libgroupwise/transferbase.cpp new file mode 100644 index 00000000..6864ea48 --- /dev/null +++ b/kopete/protocols/groupwise/libgroupwise/transferbase.cpp @@ -0,0 +1,29 @@ +/* + transferbase.cpp - Base class of all I/O transfers + + Copyright (c) 2004 SUSE Linux AG http://www.suse.com + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#include "transferbase.h" + +TransferBase::TransferBase() +{ +} + + +TransferBase::~TransferBase() +{ +} + + diff --git a/kopete/protocols/groupwise/libgroupwise/transferbase.h b/kopete/protocols/groupwise/libgroupwise/transferbase.h new file mode 100644 index 00000000..de7a688d --- /dev/null +++ b/kopete/protocols/groupwise/libgroupwise/transferbase.h @@ -0,0 +1,32 @@ +/* + transferbase.h - Base class of all I/O transfers + + Copyright (c) 2004 SUSE Linux AG http://www.suse.com + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#ifndef TRANSFERBASE_H +#define TRANSFERBASE_H + +/** +@author Kopete Developers +*/ +class TransferBase{ +public: + TransferBase(); + + ~TransferBase(); + +}; + +#endif diff --git a/kopete/protocols/groupwise/libgroupwise/userdetailsmanager.cpp b/kopete/protocols/groupwise/libgroupwise/userdetailsmanager.cpp new file mode 100644 index 00000000..2527968e --- /dev/null +++ b/kopete/protocols/groupwise/libgroupwise/userdetailsmanager.cpp @@ -0,0 +1,129 @@ +/* + userdetailsmanager.cpp - Storage of all user details seen during this session + + Copyright (c) 2004 SUSE Linux AG http://www.suse.com + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#include "client.h" +#include "tasks/getdetailstask.h" + +#include "userdetailsmanager.h" + +UserDetailsManager::UserDetailsManager( Client * parent, const char *name) + : QObject(parent, name), m_client( parent ) +{ +} + +UserDetailsManager::~UserDetailsManager() +{ +} + +void UserDetailsManager::dump( const QStringList & list ) +{ + for ( QStringList::ConstIterator it = list.begin(); it != list.end(); ++it ) + { + m_client->debug( QString( " - %1" ).arg (*it) ); + } +} + +bool UserDetailsManager::known( const QString & dn ) +{ + if ( dn == m_client->userDN() ) + return true; + // TODO: replace with m_detailsMap.contains( dn ); + QStringList::Iterator found = m_detailsMap.keys().find( dn ); + // we always know the local user's details, so don't look them up + return ( found !=m_detailsMap.keys().end() ); +} + +ContactDetails UserDetailsManager::details( const QString & dn ) +{ + return m_detailsMap[ dn ]; +} + +QStringList UserDetailsManager::knownDNs() +{ + return m_detailsMap.keys(); +} + +void UserDetailsManager::addDetails( const ContactDetails & details ) +{ + //qDebug( "UserDetailsManager::addContact, got %s, we now know: ", details.dn.ascii() ); + m_detailsMap.insert( details.dn, details ); +/* QStringList keys = m_detailsMap.keys(); + dump( keys ); + qDebug( "UserDetailsManager::addContact, pending: " ); + dump( m_pendingDNs );*/ +} + +void UserDetailsManager::removeContact( const QString & dn ) +{ + m_detailsMap.remove( dn ); +} + +void UserDetailsManager::requestDetails( const QStringList & dnList, bool onlyUnknown ) +{ + // build a list of DNs that are not already subject to a pending request + QStringList requestList; + QValueListConstIterator<QString> end = dnList.end(); + for ( QValueListConstIterator<QString> it = dnList.begin(); it != end; ++it ) + { + // don't request our own details + if ( *it == m_client->userDN() ) + break; + // don't request details we already have unless the caller specified this + if ( onlyUnknown && known( *it ) ) + break; + QStringList::Iterator found = m_pendingDNs.find( *it ); + if ( found == m_pendingDNs.end() ) + { + m_client->debug( QString( "UserDetailsManager::requestDetails - including %1" ).arg( (*it) ) ); + requestList.append( *it ); + m_pendingDNs.append( *it ); + } + } + if ( !requestList.empty() ) + { + GetDetailsTask * gdt = new GetDetailsTask( m_client->rootTask() ); + gdt->userDNs( requestList ); + connect( gdt, SIGNAL( gotContactUserDetails( const GroupWise::ContactDetails & ) ), + SLOT( slotReceiveContactDetails( const GroupWise::ContactDetails & ) ) ); + // TODO: connect to gdt's finished() signal, check for failures, expand gdt to maintain a list of not found DNs? + gdt->go( true ); + } + else + { + m_client->debug( "UserDetailsManager::requestDetails - all requested contacts are already available or pending" ); + } +} + +void UserDetailsManager::requestDetails( const QString & dn, bool onlyUnknown ) +{ + m_client->debug( QString( "UserDetailsManager::requestDetails for %1" ).arg( dn ) ); + QStringList list; + list.append( dn ); + requestDetails( list, onlyUnknown ); +} + +void UserDetailsManager::slotReceiveContactDetails( const GroupWise::ContactDetails & details ) +{ + m_client->debug( "UserDetailsManager::slotReceiveContactDetails()" ); + m_pendingDNs.remove( details.dn ); + /*client()->userDetailsManager()->*/ + addDetails( details ); + //emit temporaryContact( details ); + emit gotContactDetails( details ); +} + +#include "userdetailsmanager.moc" diff --git a/kopete/protocols/groupwise/libgroupwise/userdetailsmanager.h b/kopete/protocols/groupwise/libgroupwise/userdetailsmanager.h new file mode 100644 index 00000000..4e9b6022 --- /dev/null +++ b/kopete/protocols/groupwise/libgroupwise/userdetailsmanager.h @@ -0,0 +1,84 @@ +/* + userdetailsmanager.h - Storage of all user details seen during this session + + Copyright (c) 2004 SUSE Linux AG http://www.suse.com + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#ifndef USERDETAILSMANAGER_H +#define USERDETAILSMANAGER_H + +#include <qmap.h> +#include <qobject.h> +#include <qstringlist.h> + +#include "gwerror.h" +class Client; + +/** +Several client event handling processes require that a contact's details are available before exposing the event to the user. This class is responsible for issuing details requests, tracking which users the client already has received details for, and signalling when details have been received. The manager allows multiple interleaved get details requests to be replaced by a single request. + +@author SUSE AG +*/ + +class UserDetailsManager : public QObject +{ +Q_OBJECT +public: + UserDetailsManager( Client * parent, const char *name = 0); + ~UserDetailsManager(); + /** + * List of DNs that we have already received details for + */ + QStringList knownDNs(); + /** + * Check if we have details for a single DN + */ + bool known( const QString &dn ); + /** + * Get details for a given DN + */ + ContactDetails details( const QString &dn ); + /** + * Add a ContactDetails object to our cache. + * This SHOULD be called when receiving details in contactlist receive and manipulation, to prevent unnecessary additional requests. + */ + void addDetails( const GroupWise::ContactDetails & details ); + /** + * Remove a contact from the list of known DNs. This MUST be performed when a client removes a DN from its local contact list, + * otherwise new events from this DN will not receive user details. + */ + void removeContact( const QString & dn ); + /** + * Explicitly request details for a set of contacts from the server. + * Will signal @ref gotContactUserDetails for each one when they are available. + */ + void requestDetails( const QStringList & dnList, bool onlyUnknown = true ); + /** + * Explicitly request a contact's details from the server. Will signal @ref gotContactUserDetails when they are available. + */ + void requestDetails( const QString & dn, bool onlyUnknown = true ); + +signals: + void gotContactDetails( const GroupWise::ContactDetails & ); +protected slots: + void slotReceiveContactDetails( const GroupWise::ContactDetails & ); +protected: + void dump( const QStringList & list ); +private: + QStringList m_pendingDNs; // a list of DNs that have pending requests + Client * m_client; + QMap< QString, GroupWise::ContactDetails > m_detailsMap; +}; + +#endif diff --git a/kopete/protocols/groupwise/libgroupwise/usertransfer.cpp b/kopete/protocols/groupwise/libgroupwise/usertransfer.cpp new file mode 100644 index 00000000..85f0f395 --- /dev/null +++ b/kopete/protocols/groupwise/libgroupwise/usertransfer.cpp @@ -0,0 +1,46 @@ +/* + usertransfer.cpp - Ancestor of In- or outgoing Transfers (Requests and Response) + initated by the user. + + Copyright (c) 2004 SUSE Linux AG http://www.suse.com + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#include "usertransfer.h" + +UserTransfer::UserTransfer( int transactionId ) +{ + m_transactionId = transactionId; +} + +UserTransfer::~UserTransfer() +{ + m_fields.purge(); +} + +void UserTransfer::setFields( Field::FieldList fields ) +{ + m_fields = fields; +} + +int UserTransfer::transactionId() +{ + return m_transactionId; +} + +Field::FieldList UserTransfer::fields() +{ + return m_fields; +} + + diff --git a/kopete/protocols/groupwise/libgroupwise/usertransfer.h b/kopete/protocols/groupwise/libgroupwise/usertransfer.h new file mode 100644 index 00000000..d4d30dbc --- /dev/null +++ b/kopete/protocols/groupwise/libgroupwise/usertransfer.h @@ -0,0 +1,45 @@ +/* + usertransfer.h - Ancestor of In- or outgoing Transfers (Requests and Response) + initated by the user. + + Copyright (c) 2004 SUSE Linux AG http://www.suse.com + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#ifndef USERTRANSFER_H +#define USERTRANSFER_H + +#include "gwfield.h" + +#include "transfer.h" + +/** + * Represents transfers of data in response to a user action, either outgoing Requests, or incoming Responses + * @author Kopete Developers + */ +class UserTransfer : public Transfer +{ +public: + UserTransfer( int transactionId ); + virtual ~UserTransfer(); + int transactionId(); + Field::FieldList fields(); + void setFields( Field::FieldList fields ); + +private: + int m_transactionId; + Field::FieldList m_fields; + +}; + +#endif |