summaryrefslogtreecommitdiffstats
path: root/kopete/protocols/groupwise
diff options
context:
space:
mode:
Diffstat (limited to 'kopete/protocols/groupwise')
-rw-r--r--kopete/protocols/groupwise/DESIGN50
-rw-r--r--kopete/protocols/groupwise/Makefile.am26
-rw-r--r--kopete/protocols/groupwise/gwaccount.cpp1646
-rw-r--r--kopete/protocols/groupwise/gwaccount.h360
-rw-r--r--kopete/protocols/groupwise/gwaddui.ui113
-rw-r--r--kopete/protocols/groupwise/gwbytestream.cpp156
-rw-r--r--kopete/protocols/groupwise/gwbytestream.h69
-rw-r--r--kopete/protocols/groupwise/gwchatui.rc17
-rw-r--r--kopete/protocols/groupwise/gwconnector.cpp129
-rw-r--r--kopete/protocols/groupwise/gwconnector.h66
-rw-r--r--kopete/protocols/groupwise/gwcontact.cpp317
-rw-r--r--kopete/protocols/groupwise/gwcontact.h194
-rw-r--r--kopete/protocols/groupwise/gwcontactlist.cpp237
-rw-r--r--kopete/protocols/groupwise/gwcontactlist.h90
-rw-r--r--kopete/protocols/groupwise/gwmessagemanager.cpp516
-rw-r--r--kopete/protocols/groupwise/gwmessagemanager.h177
-rw-r--r--kopete/protocols/groupwise/gwprotocol.cpp283
-rw-r--r--kopete/protocols/groupwise/gwprotocol.h112
-rw-r--r--kopete/protocols/groupwise/icons/Makefile.am3
-rw-r--r--kopete/protocols/groupwise/icons/cr16-action-groupwise_away.pngbin0 -> 218 bytes
-rw-r--r--kopete/protocols/groupwise/icons/cr16-action-groupwise_busy.pngbin0 -> 285 bytes
-rw-r--r--kopete/protocols/groupwise/icons/cr16-action-groupwise_connecting.mngbin0 -> 1449 bytes
-rw-r--r--kopete/protocols/groupwise/icons/cr16-action-groupwise_invisible.pngbin0 -> 432 bytes
-rw-r--r--kopete/protocols/groupwise/icons/cr16-action-groupwise_online.pngbin0 -> 515 bytes
-rw-r--r--kopete/protocols/groupwise/icons/cr16-action-logging.pngbin0 -> 779 bytes
-rw-r--r--kopete/protocols/groupwise/icons/cr16-app-groupwise_protocol.pngbin0 -> 515 bytes
-rw-r--r--kopete/protocols/groupwise/icons/cr22-action-logging.pngbin0 -> 1105 bytes
-rw-r--r--kopete/protocols/groupwise/icons/cr22-app-groupwise_protocol.pngbin0 -> 686 bytes
-rw-r--r--kopete/protocols/groupwise/icons/cr32-action-logging.pngbin0 -> 1712 bytes
-rw-r--r--kopete/protocols/groupwise/icons/cr32-app-groupwise_protocol.pngbin0 -> 975 bytes
-rw-r--r--kopete/protocols/groupwise/icons/cr48-action-logging.pngbin0 -> 2884 bytes
-rw-r--r--kopete/protocols/groupwise/icons/cr48-app-groupwise_protocol.pngbin0 -> 1849 bytes
-rw-r--r--kopete/protocols/groupwise/icons/cr64-action-logging.pngbin0 -> 3249 bytes
-rw-r--r--kopete/protocols/groupwise/icons/cr64-app-groupwise_protocol.pngbin0 -> 1731 bytes
-rw-r--r--kopete/protocols/groupwise/kopete_groupwise.desktop60
-rw-r--r--kopete/protocols/groupwise/libgroupwise/Makefile.am40
-rw-r--r--kopete/protocols/groupwise/libgroupwise/bytestream.cpp269
-rw-r--r--kopete/protocols/groupwise/libgroupwise/bytestream.h78
-rw-r--r--kopete/protocols/groupwise/libgroupwise/chatroommanager.cpp140
-rw-r--r--kopete/protocols/groupwise/libgroupwise/chatroommanager.h66
-rw-r--r--kopete/protocols/groupwise/libgroupwise/client.cpp541
-rw-r--r--kopete/protocols/groupwise/libgroupwise/client.h401
-rw-r--r--kopete/protocols/groupwise/libgroupwise/connector.cpp73
-rw-r--r--kopete/protocols/groupwise/libgroupwise/connector.h61
-rw-r--r--kopete/protocols/groupwise/libgroupwise/coreprotocol.cpp507
-rw-r--r--kopete/protocols/groupwise/libgroupwise/coreprotocol.h202
-rw-r--r--kopete/protocols/groupwise/libgroupwise/eventprotocol.cpp216
-rw-r--r--kopete/protocols/groupwise/libgroupwise/eventprotocol.h130
-rw-r--r--kopete/protocols/groupwise/libgroupwise/eventtransfer.cpp145
-rw-r--r--kopete/protocols/groupwise/libgroupwise/eventtransfer.h110
-rw-r--r--kopete/protocols/groupwise/libgroupwise/gwchatrooms.h78
-rw-r--r--kopete/protocols/groupwise/libgroupwise/gwclientstream.cpp606
-rw-r--r--kopete/protocols/groupwise/libgroupwise/gwclientstream.h185
-rw-r--r--kopete/protocols/groupwise/libgroupwise/gwerror.cpp276
-rw-r--r--kopete/protocols/groupwise/libgroupwise/gwerror.h241
-rw-r--r--kopete/protocols/groupwise/libgroupwise/gwfield.cpp223
-rw-r--r--kopete/protocols/groupwise/libgroupwise/gwfield.h275
-rw-r--r--kopete/protocols/groupwise/libgroupwise/gwglobal.cpp39
-rw-r--r--kopete/protocols/groupwise/libgroupwise/inputprotocolbase.cpp112
-rw-r--r--kopete/protocols/groupwise/libgroupwise/inputprotocolbase.h78
-rw-r--r--kopete/protocols/groupwise/libgroupwise/privacymanager.cpp251
-rw-r--r--kopete/protocols/groupwise/libgroupwise/privacymanager.h88
-rw-r--r--kopete/protocols/groupwise/libgroupwise/qca/COPYING504
-rw-r--r--kopete/protocols/groupwise/libgroupwise/qca/INSTALL12
-rw-r--r--kopete/protocols/groupwise/libgroupwise/qca/Makefile.am1
-rw-r--r--kopete/protocols/groupwise/libgroupwise/qca/README29
-rw-r--r--kopete/protocols/groupwise/libgroupwise/qca/TODO6
-rw-r--r--kopete/protocols/groupwise/libgroupwise/qca/src/Makefile.am8
-rw-r--r--kopete/protocols/groupwise/libgroupwise/qca/src/qca.cpp1486
-rw-r--r--kopete/protocols/groupwise/libgroupwise/qca/src/qca.h466
-rw-r--r--kopete/protocols/groupwise/libgroupwise/qca/src/qcaprovider.h191
-rw-r--r--kopete/protocols/groupwise/libgroupwise/qcatlshandler.cpp122
-rw-r--r--kopete/protocols/groupwise/libgroupwise/qcatlshandler.h61
-rw-r--r--kopete/protocols/groupwise/libgroupwise/request.cpp34
-rw-r--r--kopete/protocols/groupwise/libgroupwise/request.h40
-rw-r--r--kopete/protocols/groupwise/libgroupwise/requestfactory.cpp37
-rw-r--r--kopete/protocols/groupwise/libgroupwise/requestfactory.h44
-rw-r--r--kopete/protocols/groupwise/libgroupwise/response.cpp34
-rw-r--r--kopete/protocols/groupwise/libgroupwise/response.h39
-rw-r--r--kopete/protocols/groupwise/libgroupwise/responseprotocol.cpp314
-rw-r--r--kopete/protocols/groupwise/libgroupwise/responseprotocol.h76
-rw-r--r--kopete/protocols/groupwise/libgroupwise/rtf.cc2532
-rw-r--r--kopete/protocols/groupwise/libgroupwise/rtf.ll866
-rw-r--r--kopete/protocols/groupwise/libgroupwise/rtf2html.h207
-rw-r--r--kopete/protocols/groupwise/libgroupwise/safedelete.cpp139
-rw-r--r--kopete/protocols/groupwise/libgroupwise/safedelete.h79
-rw-r--r--kopete/protocols/groupwise/libgroupwise/securestream.cpp542
-rw-r--r--kopete/protocols/groupwise/libgroupwise/securestream.h156
-rw-r--r--kopete/protocols/groupwise/libgroupwise/stream.cpp31
-rw-r--r--kopete/protocols/groupwise/libgroupwise/stream.h92
-rw-r--r--kopete/protocols/groupwise/libgroupwise/task.cpp268
-rw-r--r--kopete/protocols/groupwise/libgroupwise/task.h97
-rw-r--r--kopete/protocols/groupwise/libgroupwise/tasks/Makefile.am30
-rw-r--r--kopete/protocols/groupwise/libgroupwise/tasks/chatcountstask.cpp87
-rw-r--r--kopete/protocols/groupwise/libgroupwise/tasks/chatcountstask.h49
-rw-r--r--kopete/protocols/groupwise/libgroupwise/tasks/chatpropertiestask.cpp139
-rw-r--r--kopete/protocols/groupwise/libgroupwise/tasks/chatpropertiestask.h64
-rw-r--r--kopete/protocols/groupwise/libgroupwise/tasks/conferencetask.cpp230
-rw-r--r--kopete/protocols/groupwise/libgroupwise/tasks/conferencetask.h74
-rw-r--r--kopete/protocols/groupwise/libgroupwise/tasks/connectiontask.cpp55
-rw-r--r--kopete/protocols/groupwise/libgroupwise/tasks/connectiontask.h43
-rw-r--r--kopete/protocols/groupwise/libgroupwise/tasks/createconferencetask.cpp85
-rw-r--r--kopete/protocols/groupwise/libgroupwise/tasks/createconferencetask.h54
-rw-r--r--kopete/protocols/groupwise/libgroupwise/tasks/createcontactinstancetask.cpp97
-rw-r--r--kopete/protocols/groupwise/libgroupwise/tasks/createcontactinstancetask.h54
-rw-r--r--kopete/protocols/groupwise/libgroupwise/tasks/createcontacttask.cpp144
-rw-r--r--kopete/protocols/groupwise/libgroupwise/tasks/createcontacttask.h88
-rw-r--r--kopete/protocols/groupwise/libgroupwise/tasks/createfoldertask.cpp41
-rw-r--r--kopete/protocols/groupwise/libgroupwise/tasks/createfoldertask.h40
-rw-r--r--kopete/protocols/groupwise/libgroupwise/tasks/deleteitemtask.cpp46
-rw-r--r--kopete/protocols/groupwise/libgroupwise/tasks/deleteitemtask.h38
-rw-r--r--kopete/protocols/groupwise/libgroupwise/tasks/eventtask.cpp48
-rw-r--r--kopete/protocols/groupwise/libgroupwise/tasks/eventtask.h43
-rw-r--r--kopete/protocols/groupwise/libgroupwise/tasks/getchatsearchresultstask.cpp122
-rw-r--r--kopete/protocols/groupwise/libgroupwise/tasks/getchatsearchresultstask.h52
-rw-r--r--kopete/protocols/groupwise/libgroupwise/tasks/getdetailstask.cpp136
-rw-r--r--kopete/protocols/groupwise/libgroupwise/tasks/getdetailstask.h49
-rw-r--r--kopete/protocols/groupwise/libgroupwise/tasks/getstatustask.cpp72
-rw-r--r--kopete/protocols/groupwise/libgroupwise/tasks/getstatustask.h44
-rw-r--r--kopete/protocols/groupwise/libgroupwise/tasks/gwtasklogin.cpp0
-rw-r--r--kopete/protocols/groupwise/libgroupwise/tasks/gwtasklogin.h0
-rw-r--r--kopete/protocols/groupwise/libgroupwise/tasks/joinchattask.cpp131
-rw-r--r--kopete/protocols/groupwise/libgroupwise/tasks/joinchattask.h52
-rw-r--r--kopete/protocols/groupwise/libgroupwise/tasks/joinconferencetask.cpp175
-rw-r--r--kopete/protocols/groupwise/libgroupwise/tasks/joinconferencetask.h54
-rw-r--r--kopete/protocols/groupwise/libgroupwise/tasks/keepalivetask.cpp42
-rw-r--r--kopete/protocols/groupwise/libgroupwise/tasks/keepalivetask.h38
-rw-r--r--kopete/protocols/groupwise/libgroupwise/tasks/leaveconferencetask.cpp40
-rw-r--r--kopete/protocols/groupwise/libgroupwise/tasks/leaveconferencetask.h40
-rw-r--r--kopete/protocols/groupwise/libgroupwise/tasks/logintask.cpp360
-rw-r--r--kopete/protocols/groupwise/libgroupwise/tasks/logintask.h64
-rw-r--r--kopete/protocols/groupwise/libgroupwise/tasks/modifycontactlisttask.cpp139
-rw-r--r--kopete/protocols/groupwise/libgroupwise/tasks/modifycontactlisttask.h51
-rw-r--r--kopete/protocols/groupwise/libgroupwise/tasks/movecontacttask.cpp83
-rw-r--r--kopete/protocols/groupwise/libgroupwise/tasks/movecontacttask.h49
-rw-r--r--kopete/protocols/groupwise/libgroupwise/tasks/needfoldertask.cpp58
-rw-r--r--kopete/protocols/groupwise/libgroupwise/tasks/needfoldertask.h39
-rw-r--r--kopete/protocols/groupwise/libgroupwise/tasks/pollsearchresultstask.cpp185
-rw-r--r--kopete/protocols/groupwise/libgroupwise/tasks/pollsearchresultstask.h52
-rw-r--r--kopete/protocols/groupwise/libgroupwise/tasks/privacyitemtask.cpp82
-rw-r--r--kopete/protocols/groupwise/libgroupwise/tasks/privacyitemtask.h50
-rw-r--r--kopete/protocols/groupwise/libgroupwise/tasks/rejectinvitetask.cpp39
-rw-r--r--kopete/protocols/groupwise/libgroupwise/tasks/rejectinvitetask.h41
-rw-r--r--kopete/protocols/groupwise/libgroupwise/tasks/requesttask.cpp76
-rw-r--r--kopete/protocols/groupwise/libgroupwise/tasks/requesttask.h42
-rw-r--r--kopete/protocols/groupwise/libgroupwise/tasks/searchchattask.cpp127
-rw-r--r--kopete/protocols/groupwise/libgroupwise/tasks/searchchattask.h66
-rw-r--r--kopete/protocols/groupwise/libgroupwise/tasks/searchusertask.cpp137
-rw-r--r--kopete/protocols/groupwise/libgroupwise/tasks/searchusertask.h63
-rw-r--r--kopete/protocols/groupwise/libgroupwise/tasks/sendinvitetask.cpp42
-rw-r--r--kopete/protocols/groupwise/libgroupwise/tasks/sendinvitetask.h43
-rw-r--r--kopete/protocols/groupwise/libgroupwise/tasks/sendmessagetask.cpp51
-rw-r--r--kopete/protocols/groupwise/libgroupwise/tasks/sendmessagetask.h41
-rw-r--r--kopete/protocols/groupwise/libgroupwise/tasks/setstatustask.cpp69
-rw-r--r--kopete/protocols/groupwise/libgroupwise/tasks/setstatustask.h46
-rw-r--r--kopete/protocols/groupwise/libgroupwise/tasks/statustask.cpp47
-rw-r--r--kopete/protocols/groupwise/libgroupwise/tasks/statustask.h40
-rw-r--r--kopete/protocols/groupwise/libgroupwise/tasks/tests/Makefile.am7
-rw-r--r--kopete/protocols/groupwise/libgroupwise/tasks/tests/task_take_test.cpp18
-rw-r--r--kopete/protocols/groupwise/libgroupwise/tasks/typingtask.cpp44
-rw-r--r--kopete/protocols/groupwise/libgroupwise/tasks/typingtask.h41
-rw-r--r--kopete/protocols/groupwise/libgroupwise/tasks/updatecontacttask.cpp76
-rw-r--r--kopete/protocols/groupwise/libgroupwise/tasks/updatecontacttask.h44
-rw-r--r--kopete/protocols/groupwise/libgroupwise/tasks/updatefoldertask.cpp59
-rw-r--r--kopete/protocols/groupwise/libgroupwise/tasks/updatefoldertask.h42
-rw-r--r--kopete/protocols/groupwise/libgroupwise/tasks/updateitemtask.cpp39
-rw-r--r--kopete/protocols/groupwise/libgroupwise/tasks/updateitemtask.h40
-rw-r--r--kopete/protocols/groupwise/libgroupwise/tests/Makefile.am22
-rw-r--r--kopete/protocols/groupwise/libgroupwise/tests/client_test.cpp10
-rw-r--r--kopete/protocols/groupwise/libgroupwise/tests/clientstream_test.cpp107
-rw-r--r--kopete/protocols/groupwise/libgroupwise/tests/clientstream_test.h57
-rw-r--r--kopete/protocols/groupwise/libgroupwise/tests/coreprotocol_test.cpp30
-rw-r--r--kopete/protocols/groupwise/libgroupwise/tests/field_test.cpp154
-rw-r--r--kopete/protocols/groupwise/libgroupwise/tlshandler.cpp31
-rw-r--r--kopete/protocols/groupwise/libgroupwise/tlshandler.h51
-rw-r--r--kopete/protocols/groupwise/libgroupwise/transfer.cpp30
-rw-r--r--kopete/protocols/groupwise/libgroupwise/transfer.h34
-rw-r--r--kopete/protocols/groupwise/libgroupwise/transferbase.cpp29
-rw-r--r--kopete/protocols/groupwise/libgroupwise/transferbase.h32
-rw-r--r--kopete/protocols/groupwise/libgroupwise/userdetailsmanager.cpp129
-rw-r--r--kopete/protocols/groupwise/libgroupwise/userdetailsmanager.h84
-rw-r--r--kopete/protocols/groupwise/libgroupwise/usertransfer.cpp46
-rw-r--r--kopete/protocols/groupwise/libgroupwise/usertransfer.h45
-rw-r--r--kopete/protocols/groupwise/ui/Makefile.am19
-rw-r--r--kopete/protocols/groupwise/ui/gwaccountpreferences.ui320
-rw-r--r--kopete/protocols/groupwise/ui/gwaddcontactpage.cpp108
-rw-r--r--kopete/protocols/groupwise/ui/gwaddcontactpage.h68
-rw-r--r--kopete/protocols/groupwise/ui/gwaddui.ui137
-rw-r--r--kopete/protocols/groupwise/ui/gwchatpropsdialog.cpp122
-rw-r--r--kopete/protocols/groupwise/ui/gwchatpropsdialog.h69
-rw-r--r--kopete/protocols/groupwise/ui/gwchatpropswidget.ui394
-rw-r--r--kopete/protocols/groupwise/ui/gwchatsearchdialog.cpp106
-rw-r--r--kopete/protocols/groupwise/ui/gwchatsearchdialog.h49
-rw-r--r--kopete/protocols/groupwise/ui/gwchatsearchwidget.ui116
-rw-r--r--kopete/protocols/groupwise/ui/gwcontactproperties.cpp144
-rw-r--r--kopete/protocols/groupwise/ui/gwcontactproperties.h60
-rw-r--r--kopete/protocols/groupwise/ui/gwcontactpropswidget.ui211
-rw-r--r--kopete/protocols/groupwise/ui/gwcontactsearch.ui386
-rw-r--r--kopete/protocols/groupwise/ui/gwcustomstatusedit.ui92
-rw-r--r--kopete/protocols/groupwise/ui/gwcustomstatuswidget.ui112
-rw-r--r--kopete/protocols/groupwise/ui/gweditaccountwidget.cpp136
-rw-r--r--kopete/protocols/groupwise/ui/gweditaccountwidget.h64
-rw-r--r--kopete/protocols/groupwise/ui/gwprivacy.ui193
-rw-r--r--kopete/protocols/groupwise/ui/gwprivacydialog.cpp349
-rw-r--r--kopete/protocols/groupwise/ui/gwprivacydialog.h67
-rw-r--r--kopete/protocols/groupwise/ui/gwreceiveinvitationdialog.cpp77
-rw-r--r--kopete/protocols/groupwise/ui/gwreceiveinvitationdialog.h48
-rw-r--r--kopete/protocols/groupwise/ui/gwsearch.cpp281
-rw-r--r--kopete/protocols/groupwise/ui/gwsearch.h58
-rw-r--r--kopete/protocols/groupwise/ui/gwshowinvitation.ui135
210 files changed, 28565 insertions, 0 deletions
diff --git a/kopete/protocols/groupwise/DESIGN b/kopete/protocols/groupwise/DESIGN
new file mode 100644
index 00000000..aa976191
--- /dev/null
+++ b/kopete/protocols/groupwise/DESIGN
@@ -0,0 +1,50 @@
+Plan for updating Kopete's groupwise support for GW7.
+
+Review protocol DONE
+
+libgroupwise:
+
+Update error codes DONE
+Extend event protocol to handle new event formats PROGRESS
+Make protocol version selectable in Client DONE
+Add new event tasks
+ Event codes DONE
+ Broadcast in ConferenceTask - small Event proto update DONE
+ Chat events in ChatroomTask
+ ConfAttribUpdate - large Event proto update
+ ConfTopicChanged
+ ChatroomNameChanged - large Event proto update
+ ConfRightsChanged - vlarge Event proto update
+ ConfRemoved - large Event proto update and more info from Mike.
+ ChatOwner changed - vlarge Event proto update
+(large update -> requires new EventTransfer subclass)
+
+Update Client with event signals
+ Broadcast DONE
+ Chatroom
+
+Add request tasks
+
+Update Attribs
+
+Search for chats
+ Get Results
+ Stop Search
+ Update Numbers
+
+Add Client interface for requests
+
+kopete_groupwise:
+
+Handle broadcasts DONE
+
+Set custom statuses on the server
+
+search for chats
+
+join chat
+
+create chat
+
+modify chat
+
diff --git a/kopete/protocols/groupwise/Makefile.am b/kopete/protocols/groupwise/Makefile.am
new file mode 100644
index 00000000..0c8a38fc
--- /dev/null
+++ b/kopete/protocols/groupwise/Makefile.am
@@ -0,0 +1,26 @@
+
+
+SUBDIRS = icons libgroupwise ui
+
+AM_CPPFLAGS = $(KOPETE_INCLUDES) $(all_includes)
+
+METASOURCES = AUTO
+
+noinst_HEADERS = gwprotocol.h gwcontact.h gwaccount.h gwbytestream.h \
+ gwconnector.h gwmessagemanager.h gwcontactlist.h
+kde_module_LTLIBRARIES = kopete_groupwise.la
+kopete_groupwise_la_SOURCES = gwprotocol.cpp gwcontact.cpp gwaccount.cpp \
+ gwbytestream.cpp gwconnector.cpp gwmessagemanager.cpp gwcontactlist.cpp
+kopete_groupwise_la_LDFLAGS = -module -no-undefined $(KDE_PLUGIN) \
+ $(all_libraries)
+kopete_groupwise_la_LIBADD = ui/libkopetegroupwiseui.la \
+ libgroupwise/libgroupwise.la ../../libkopete/libkopete.la $(LIB_KIO)
+
+service_DATA = kopete_groupwise.desktop
+servicedir = $(kde_servicesdir)
+INCLUDES = -I$(top_srcdir)/kopete/protocols/groupwise/libgroupwise/tasks \
+ -I$(top_srcdir)/kopete/protocols/groupwise/libgroupwise/qca/src -I$(top_srcdir)/kopete/protocols/groupwise/libgroupwise \
+ -I$(top_srcdir)/kopete/protocols/groupwise/ui -I$(top_builddir)/kopete/protocols/groupwise/ui
+
+mydatadir = $(kde_datadir)/kopete_groupwise
+mydata_DATA = gwchatui.rc
diff --git a/kopete/protocols/groupwise/gwaccount.cpp b/kopete/protocols/groupwise/gwaccount.cpp
new file mode 100644
index 00000000..48ef3833
--- /dev/null
+++ b/kopete/protocols/groupwise/gwaccount.cpp
@@ -0,0 +1,1646 @@
+/*
+ gwaccount.cpp - Kopete GroupWise Protocol
+
+ Copyright (c) 2004 SUSE Linux AG http://www.suse.com
+
+ Based on Testbed
+ Copyright (c) 2003 by Will Stephenson <[email protected]>
+
+ Kopete (c) 2002-2003 by the Kopete developers <[email protected]>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include <sys/utsname.h>
+
+#include <qvalidator.h>
+
+#include <kaboutdata.h>
+#include <kapplication.h>
+#include <kconfig.h>
+#include <kdebug.h>
+#include <kdialogbase.h>
+#include <kinputdialog.h>
+#include <klocale.h>
+#include <kmessagebox.h>
+#include <kpassivepopup.h>
+
+#include <kopeteuiglobal.h>
+#include <kopeteaway.h>
+#include <kopeteawayaction.h>
+#include <kopetecontactlist.h>
+#include <kopetegroup.h>
+#include <kopeteglobal.h>
+#include <kopetemetacontact.h>
+#include <kopetepassword.h>
+#include <kopeteview.h>
+
+#include "client.h"
+#include "qca.h"
+#include "gwcontact.h"
+#include "gwcontactlist.h"
+#include "gwprotocol.h"
+#include "gwconnector.h"
+#include "gwmessagemanager.h"
+#include "privacymanager.h"
+#include "qcatlshandler.h"
+#include "userdetailsmanager.h"
+#include "tasks/createcontacttask.h"
+#include "tasks/createcontactinstancetask.h"
+#include "tasks/deleteitemtask.h"
+#include "tasks/movecontacttask.h"
+#include "tasks/updatecontacttask.h"
+#include "tasks/updatefoldertask.h"
+#include "ui/gwchatsearchdialog.h"
+#include "ui/gwprivacy.h"
+#include "ui/gwprivacydialog.h"
+#include "ui/gwreceiveinvitationdialog.h"
+
+#include "gwaccount.h"
+
+GroupWiseAccount::GroupWiseAccount( GroupWiseProtocol *parent, const QString& accountID, const char *name )
+: Kopete::ManagedConnectionAccount ( parent, accountID, 0, "groupwiseaccount" )
+{
+ Q_UNUSED( name );
+ // Init the myself contact
+ setMyself( new GroupWiseContact( this, accountId(), Kopete::ContactList::self()->myself(), 0, 0, 0 ) );
+ myself()->setOnlineStatus( GroupWiseProtocol::protocol()->groupwiseOffline );
+
+ // Contact list management
+ QObject::connect( Kopete::ContactList::self(), SIGNAL( groupRenamed( Kopete::Group *, const QString & ) ),
+ SLOT( slotKopeteGroupRenamed( Kopete::Group * ) ) );
+ QObject::connect( Kopete::ContactList::self(), SIGNAL( groupRemoved( Kopete::Group * ) ),
+ SLOT( slotKopeteGroupRemoved( Kopete::Group * ) ) );
+
+ m_actionAutoReply = new KAction ( i18n( "&Set Auto-Reply..." ), QString::null, 0, this,
+ SLOT( slotSetAutoReply() ), this, "actionSetAutoReply");
+ m_actionJoinChatRoom = new KAction ( i18n( "&Join Channel..." ), QString::null, 0, this,
+ SLOT( slotJoinChatRoom() ), this, "actionJoinChatRoom");
+ m_actionManagePrivacy = new KAction ( i18n( "&Manage Privacy..." ), QString::null, 0, this,
+ SLOT( slotPrivacy() ), this, "actionPrivacy");
+
+ m_connector = 0;
+ m_QCATLS = 0;
+ m_tlsHandler = 0;
+ m_clientStream = 0;
+ m_client = 0;
+ m_dontSync = false;
+ m_serverListModel = 0;
+}
+
+GroupWiseAccount::~GroupWiseAccount()
+{
+ cleanup();
+}
+
+KActionMenu* GroupWiseAccount::actionMenu()
+{
+ KActionMenu *m_actionMenu=Kopete::Account::actionMenu();
+
+ m_actionAutoReply->setEnabled( isConnected() );
+ m_actionManagePrivacy->setEnabled( isConnected() );
+ m_actionJoinChatRoom->setEnabled( isConnected() );
+ m_actionMenu->insert( m_actionManagePrivacy );
+ m_actionMenu->insert( m_actionAutoReply );
+ m_actionMenu->insert( m_actionJoinChatRoom );
+ /* Used for debugging */
+ /*
+ theActionMenu->insert( new KAction ( "Test rtfize()", QString::null, 0, this,
+ SLOT( slotTestRTFize() ), this,
+ "actionTestRTFize") );
+ */
+ return m_actionMenu;
+}
+
+int GroupWiseAccount::port() const
+{
+ return configGroup()->readNumEntry( "Port" );
+}
+
+const QString GroupWiseAccount::server() const
+{
+ return configGroup()->readEntry( "Server" );
+}
+
+Client * GroupWiseAccount::client() const
+{
+ return m_client;
+}
+
+GroupWiseProtocol *GroupWiseAccount::protocol() const
+{
+ return static_cast<GroupWiseProtocol *>( Kopete::Account::protocol() );
+}
+
+GroupWiseChatSession * GroupWiseAccount::chatSession( Kopete::ContactPtrList others, const GroupWise::ConferenceGuid & guid, Kopete::Contact::CanCreateFlags canCreate )
+{
+ GroupWiseChatSession * chatSession = 0;
+ do // one iteration misuse of do...while to enable an easy drop-out once we locate a manager
+ {
+ // do we have a manager keyed by GUID?
+ if ( !guid.isEmpty() )
+ {
+ chatSession = findChatSessionByGuid( guid );
+ if ( chatSession )
+ {
+ kdDebug( GROUPWISE_DEBUG_GLOBAL ) << k_funcinfo << " found a message manager by GUID: " << guid << endl;
+ break;
+ }
+ }
+ // does the factory know about one, going on the chat members?
+ chatSession = dynamic_cast<GroupWiseChatSession*>(
+ Kopete::ChatSessionManager::self()->findChatSession( myself(), others, protocol() ) );
+ if ( chatSession )
+ {
+ kdDebug( GROUPWISE_DEBUG_GLOBAL ) << k_funcinfo << " found a message manager by members with GUID: " << chatSession->guid() << endl;
+ // re-add the returning contact(s) (very likely only one) to the chat
+ Kopete::Contact * returningContact;
+ for ( returningContact = others.first(); returningContact; returningContact = others.next() )
+ chatSession->joined( static_cast<GroupWiseContact *>( returningContact ) );
+
+ if ( !guid.isEmpty() )
+ chatSession->setGuid( guid );
+ break;
+ }
+ // we don't have an existing message manager for this chat, so create one if we may
+ if ( canCreate )
+ {
+ chatSession = new GroupWiseChatSession( myself(), others, protocol(), guid );
+ kdDebug( GROUPWISE_DEBUG_GLOBAL ) << k_funcinfo <<
+ " created a new message manager with GUID: " << chatSession->guid() << endl;
+ m_chatSessions.append( chatSession );
+ // listen for the message manager telling us that the user
+ //has left the conference so we remove it from our map
+ QObject::connect( chatSession, SIGNAL( leavingConference( GroupWiseChatSession * ) ),
+ SLOT( slotLeavingConference( GroupWiseChatSession * ) ) );
+ break;
+ }
+ //kdDebug( GROUPWISE_DEBUG_GLOBAL ) << k_funcinfo <<
+ // " no message manager available." << endl;
+ }
+ while ( 0 );
+ //dumpManagers();
+ return chatSession;
+}
+
+GroupWiseChatSession * GroupWiseAccount::findChatSessionByGuid( const GroupWise::ConferenceGuid & guid )
+{
+ GroupWiseChatSession * chatSession = 0;
+ QValueList<GroupWiseChatSession *>::ConstIterator it;
+ for ( it = m_chatSessions.begin(); it != m_chatSessions.end(); ++it )
+ {
+ if ( (*it)->guid() == guid )
+ {
+ chatSession = *it;
+ break;
+ }
+ }
+ return chatSession;
+}
+
+GroupWiseContact * GroupWiseAccount::contactForDN( const QString & dn )
+{
+ QDictIterator<Kopete::Contact> it( contacts() );
+ // check if we have a DN for them
+ for( ; it.current(); ++it )
+ {
+ GroupWiseContact * candidate = static_cast<GroupWiseContact*>( it.current() );
+ if ( candidate && candidate->dn() == dn )
+ return candidate;
+ }
+ // we might have just added the contact with a user ID, try the first section of the dotted dn
+ return static_cast< GroupWiseContact * >( contacts()[ protocol()->dnToDotted( dn ).section( '.', 0, 0 ) ] );
+}
+
+void GroupWiseAccount::setAway( bool away, const QString & reason )
+{
+ if ( away )
+ {
+ if ( Kopete::Away::getInstance()->idleTime() > 10 ) // don't go AwayIdle unless the user has actually been idle this long
+ setOnlineStatus( protocol()->groupwiseAwayIdle, QString::null );
+ else
+ setOnlineStatus( protocol()->groupwiseAway, reason );
+ }
+ else
+ setOnlineStatus( protocol()->groupwiseAvailable );
+}
+
+void GroupWiseAccount::performConnectWithPassword( const QString &password )
+{
+ if ( password.isEmpty() )
+ {
+ disconnect();
+ return;
+ }
+ // don't try and connect if we are already connected
+ if ( isConnected () )
+ return;
+
+ bool sslPossible = QCA::isSupported(QCA::CAP_TLS);
+
+ if (!sslPossible)
+ {
+ KMessageBox::queuedMessageBox(Kopete::UI::Global::mainWidget (), KMessageBox::Error,
+ i18n ("SSL support could not be initialized for account %1. This is most likely because the QCA TLS plugin is not installed on your system.").
+ arg(myself()->contactId()),
+ i18n ("GroupWise SSL Error"));
+ return;
+ }
+ if ( m_client )
+ {
+ m_client->close();
+ cleanup();
+ }
+ // set up network classes
+ m_connector = new KNetworkConnector( 0 );
+ //myConnector->setOptHostPort( "localhost", 8300 );
+ m_connector->setOptHostPort( server(), port() );
+ m_connector->setOptSSL( true );
+ Q_ASSERT( QCA::isSupported(QCA::CAP_TLS) );
+ m_QCATLS = new QCA::TLS;
+ m_tlsHandler = new QCATLSHandler( m_QCATLS );
+ m_clientStream = new ClientStream( m_connector, m_tlsHandler, 0);
+
+ QObject::connect( m_connector, SIGNAL( error() ), this, SLOT( slotConnError() ) );
+ QObject::connect( m_connector, SIGNAL( connected() ), this, SLOT( slotConnConnected() ) );
+
+ QObject::connect (m_clientStream, SIGNAL (connectionClosed()),
+ this, SLOT (slotCSDisconnected()));
+ QObject::connect (m_clientStream, SIGNAL (delayedCloseFinished()),
+ this, SLOT (slotCSDisconnected()));
+ // Notify us when the transport layer is connected
+ QObject::connect( m_clientStream, SIGNAL( connected() ), SLOT( slotCSConnected() ) );
+ // it's necessary to catch this signal and tell the TLS handler to proceed
+ // even if we don't check cert validity
+ QObject::connect( m_tlsHandler, SIGNAL(tlsHandshaken()), SLOT( slotTLSHandshaken()) );
+ // starts the client once the security layer is up, but see below
+ QObject::connect( m_clientStream, SIGNAL( securityLayerActivated(int) ), SLOT( slotTLSReady(int) ) );
+ // we could handle login etc in start(), in which case we would emit this signal after that
+ //QObject::connect (jabberClientStream, SIGNAL (authenticated()),
+ // this, SLOT (slotCSAuthenticated ()));
+ // we could also get do the actual login in response to this..
+ //QObject::connect (m_clientStream, SIGNAL (needAuthParams(bool, bool, bool)),
+ // this, SLOT (slotCSNeedAuthParams (bool, bool, bool)));
+
+ // not implemented: warning
+ QObject::connect( m_clientStream, SIGNAL( warning(int) ), SLOT( slotCSWarning(int) ) );
+ // not implemented: error
+ QObject::connect( m_clientStream, SIGNAL( error(int) ), SLOT( slotCSError(int) ) );
+
+ m_client = new Client( 0, CMSGPRES_GW_6_5 );
+
+ // NB these are prefixed with QObject:: to avoid any chance of a clash with our connect() methods.
+ // we connected successfully
+ QObject::connect( m_client, SIGNAL( loggedIn() ), SLOT( slotLoggedIn() ) );
+ // or connection failed
+ QObject::connect( m_client, SIGNAL( loginFailed() ), SLOT( slotLoginFailed() ) );
+ // folder listed
+ QObject::connect( m_client, SIGNAL( folderReceived( const FolderItem & ) ), SLOT( receiveFolder( const FolderItem & ) ) );
+ // contact listed
+ QObject::connect( m_client, SIGNAL( contactReceived( const ContactItem & ) ), SLOT( receiveContact( const ContactItem & ) ) );
+ // contact details listed
+ QObject::connect( m_client, SIGNAL( contactUserDetailsReceived( const GroupWise::ContactDetails & ) ), SLOT( receiveContactUserDetails( const GroupWise::ContactDetails & ) ) );
+ // contact status changed
+ QObject::connect( m_client, SIGNAL( statusReceived( const QString &, Q_UINT16, const QString & ) ), SLOT( receiveStatus( const QString &, Q_UINT16 , const QString & ) ) );
+ // incoming message
+ QObject::connect( m_client, SIGNAL( messageReceived( const ConferenceEvent & ) ), SLOT( handleIncomingMessage( const ConferenceEvent & ) ) );
+ // auto reply to one of our messages because the recipient is away
+ QObject::connect( m_client, SIGNAL( autoReplyReceived( const ConferenceEvent & ) ), SLOT( handleIncomingMessage( const ConferenceEvent & ) ) );
+
+ QObject::connect( m_client, SIGNAL( ourStatusChanged( GroupWise::Status, const QString &, const QString & ) ), SLOT( changeOurStatus( GroupWise::Status, const QString &, const QString & ) ) );
+ // conference events
+ QObject::connect( m_client,
+ SIGNAL( conferenceCreated( const int, const GroupWise::ConferenceGuid & ) ),
+ SIGNAL( conferenceCreated( const int, const GroupWise::ConferenceGuid & ) ) );
+ QObject::connect( m_client, SIGNAL( conferenceCreationFailed( const int, const int ) ), SIGNAL( conferenceCreationFailed( const int, const int ) ) );
+ QObject::connect( m_client, SIGNAL( invitationReceived( const ConferenceEvent & ) ), SLOT( receiveInvitation( const ConferenceEvent & ) ) );
+ QObject::connect( m_client, SIGNAL( conferenceLeft( const ConferenceEvent & ) ), SLOT( receiveConferenceLeft( const ConferenceEvent & ) ) );
+ QObject::connect( m_client, SIGNAL( conferenceJoinNotifyReceived( const ConferenceEvent & ) ), SLOT( receiveConferenceJoinNotify( const ConferenceEvent & ) ) );
+ QObject::connect( m_client, SIGNAL( inviteNotifyReceived( const ConferenceEvent & ) ), SLOT( receiveInviteNotify( const ConferenceEvent & ) ) );
+ QObject::connect( m_client, SIGNAL( invitationDeclined( const ConferenceEvent & ) ), SLOT( receiveInviteDeclined( const ConferenceEvent & ) ) );
+
+ QObject::connect( m_client, SIGNAL( conferenceJoined( const GroupWise::ConferenceGuid &, const QStringList &, const QStringList & ) ), SLOT( receiveConferenceJoin( const GroupWise::ConferenceGuid &, const QStringList & , const QStringList & ) ) );
+
+ // typing events
+ QObject::connect( m_client, SIGNAL( contactTyping( const ConferenceEvent & ) ),
+ SIGNAL( contactTyping( const ConferenceEvent & ) ) );
+ QObject::connect( m_client, SIGNAL( contactNotTyping( const ConferenceEvent & ) ),
+ SIGNAL( contactNotTyping( const ConferenceEvent & ) ) );
+ // misc
+ QObject::connect( m_client, SIGNAL( accountDetailsReceived( const GroupWise::ContactDetails &) ), SLOT( receiveAccountDetails( const GroupWise::ContactDetails & ) ) );
+ QObject::connect( m_client, SIGNAL( connectedElsewhere() ), SLOT( slotConnectedElsewhere() ) );
+ // privacy - contacts can't connect directly to this signal because myself() is initialised before m_client
+ QObject::connect( m_client->privacyManager(), SIGNAL( privacyChanged( const QString &, bool ) ), SIGNAL( privacyChanged( const QString &, bool ) ) );
+
+ // GW7
+ QObject::connect( m_client, SIGNAL( broadcastReceived( const ConferenceEvent & ) ), SLOT( handleIncomingMessage( const ConferenceEvent & ) ) );
+ QObject::connect( m_client, SIGNAL( systemBroadcastReceived( const ConferenceEvent & ) ), SLOT( handleIncomingMessage( const ConferenceEvent & ) ) );
+
+ struct utsname utsBuf;
+ uname (&utsBuf);
+ m_client->setClientName ("Kopete");
+ m_client->setClientVersion ( kapp->aboutData ()->version () );
+ m_client->setOSName (QString ("%1 %2").arg (utsBuf.sysname, 1).arg (utsBuf.release, 2));
+
+ kdDebug ( GROUPWISE_DEBUG_GLOBAL ) << k_funcinfo << "Connecting to GroupWise server " << server() << ":" << port() << endl;
+
+ NovellDN dn;
+ dn.dn = "maeuschen";
+ dn.server = "reiser.suse.de";
+ m_serverListModel = new GWContactList( this );
+ myself()->setOnlineStatus( protocol()->groupwiseConnecting );
+ m_client->connectToServer( m_clientStream, dn, true );
+
+ QObject::connect( m_client, SIGNAL( messageSendingFailed() ), SLOT( slotMessageSendingFailed() ) );
+}
+
+void GroupWiseAccount::slotMessageSendingFailed()
+{
+ KMessageBox::queuedMessageBox( Kopete::UI::Global::mainWidget(), KMessageBox::Sorry,
+ i18n("Message Sending Failed", "Kopete was not able to send the last message sent on account '%1'.\nIf possible, please send the console output from Kopete to <[email protected]> for analysis." ).arg( accountId() ) , i18n ("Unable to Send Message on Account '%1'").arg( accountId() ) );
+}
+
+void GroupWiseAccount::setOnlineStatus( const Kopete::OnlineStatus& status, const QString &reason )
+{
+ kdDebug ( GROUPWISE_DEBUG_GLOBAL ) << k_funcinfo << endl;
+ if ( status == protocol()->groupwiseUnknown
+ || status == protocol()->groupwiseConnecting
+ || status == protocol()->groupwiseInvalid )
+ {
+ kdDebug( GROUPWISE_DEBUG_GLOBAL ) << k_funcinfo << " called with invalid status \""
+ << status.description() << "\"" << endl;
+ }
+ // going offline
+ else if ( status == protocol()->groupwiseOffline )
+ {
+ kdDebug( GROUPWISE_DEBUG_GLOBAL ) << k_funcinfo << " DISCONNECTING" << endl;
+ disconnect();
+ }
+ // changing status
+ else if ( isConnected() )
+ {
+ kdDebug ( GROUPWISE_DEBUG_GLOBAL ) << k_funcinfo << "changing status to \"" << status.description() << "\"" << endl;
+ // Appear Offline is achieved by explicitly setting the status to offline,
+ // rather than disconnecting as when really going offline.
+ if ( status == protocol()->groupwiseAppearOffline )
+ m_client->setStatus( GroupWise::Offline, reason, configGroup()->readEntry( "AutoReply" ) );
+ else
+ m_client->setStatus( ( GroupWise::Status )status.internalStatus(), reason, configGroup()->readEntry( "AutoReply" ) );
+ }
+ // going online
+ else
+ {
+ kdDebug ( GROUPWISE_DEBUG_GLOBAL ) << k_funcinfo << "Must be connected before changing status" << endl;
+ m_initialReason = reason;
+ connect( status );
+ }
+}
+
+void GroupWiseAccount::disconnect ()
+{
+ disconnect ( Manual );
+}
+
+void GroupWiseAccount::disconnect( Kopete::Account::DisconnectReason reason )
+{
+ kdDebug( GROUPWISE_DEBUG_GLOBAL ) << k_funcinfo << endl;
+
+ if( isConnected () )
+ {
+ kdDebug (GROUPWISE_DEBUG_GLOBAL) << k_funcinfo << "Still connected, closing connection..." << endl;
+ QValueList<GroupWiseChatSession *>::ConstIterator it;
+ for ( it = m_chatSessions.begin() ; it != m_chatSessions.end(); ++it )
+ (*it)->setClosed();
+
+ /* Tell backend class to disconnect. */
+ m_client->close ();
+ }
+
+ // clear the model of the server side contact list, so that when we reconnect, there will not be any stale entries to confuse GroupWiseContact::syncGroups()
+ delete m_serverListModel;
+ m_serverListModel = 0;
+
+ // make sure that the connection animation gets stopped if we're still
+ // in the process of connecting
+ myself()->setOnlineStatus( GroupWiseProtocol::protocol()->groupwiseOffline );
+
+ disconnected( reason );
+ kdDebug(GROUPWISE_DEBUG_GLOBAL) << k_funcinfo << "Disconnected." << endl;
+}
+
+void GroupWiseAccount::cleanup()
+{
+ delete m_client;
+ delete m_clientStream;
+ delete m_QCATLS;
+ delete m_connector;
+
+ m_connector = 0;
+ m_QCATLS = 0;
+ m_clientStream = 0;
+ m_client = 0;
+}
+
+void GroupWiseAccount::createConference( const int clientId, const QStringList& invitees )
+{
+ kdDebug ( GROUPWISE_DEBUG_GLOBAL ) << k_funcinfo << endl;
+ // TODO: remove this it prevents sending a list of participants with the createconf
+ if ( isConnected() )
+ m_client->createConference( clientId , invitees );
+}
+
+void GroupWiseAccount::sendInvitation( const GroupWise::ConferenceGuid & guid, const QString & dn, const QString & message )
+{
+ kdDebug ( GROUPWISE_DEBUG_GLOBAL ) << k_funcinfo << endl;
+ if ( isConnected() )
+ {
+ GroupWise::OutgoingMessage msg;
+ msg.guid = guid;
+ msg.message = message;
+ m_client->sendInvitation( guid, dn, msg );
+ }
+}
+
+void GroupWiseAccount::slotLoggedIn()
+{
+ reconcileOfflineChanges();
+ // set local status display
+ myself()->setOnlineStatus( protocol()->groupwiseAvailable );
+ // set status on server
+ if ( initialStatus() != Kopete::OnlineStatus(Kopete::OnlineStatus::Online) &&
+ ( ( GroupWise::Status )initialStatus().internalStatus() != GroupWise::Unknown ) )
+ {
+ kdDebug( GROUPWISE_DEBUG_GLOBAL ) << "Initial status is not online, setting status to " << initialStatus().internalStatus() << endl;
+ m_client->setStatus( ( GroupWise::Status )initialStatus().internalStatus(), m_initialReason, configGroup()->readEntry( "AutoReply" ) );
+ }
+}
+
+void GroupWiseAccount::reconcileOfflineChanges()
+{
+ kdDebug ( GROUPWISE_DEBUG_GLOBAL ) << k_funcinfo << endl;
+ m_dontSync = true;
+ //sanity check the server side model vs our contact list.
+ //Contacts might have been removed from some groups or entirely on the server.
+ //Any contact not present on the server should be deleted locally.
+
+ // for each metacontact group membership:
+ // for each GroupWiseContact
+ // get its contact list instances
+ // get its metacontact's groups
+ // for each group
+ // is there no CLI with the same id?
+ // if MC has no other contacts
+ // if MC's groups size is 1
+ // remove MC
+ // else
+ // remove from group
+ // else
+ // if MC's groups size is 1 and group is topLevel
+ // remove contact
+ // else // Contact's group membership were changed elsewhere, but we can't change it here without
+ // // affecting other protocols' contacts
+ // set flag to warn user that incompatible changes were made on other client
+ bool conflicts = false;
+ QDictIterator<Kopete::Contact> it( contacts() );
+ for ( ; it.current(); ++it )
+ {
+ if ( *it == myself() )
+ continue;
+
+ GroupWiseContact * c = static_cast< GroupWiseContact *>( *it );
+ GWContactInstanceList instances = m_serverListModel->instancesWithDn( c->dn() );
+ QPtrList<Kopete::Group> groups = c->metaContact()->groups();
+ QPtrListIterator<Kopete::Group> grpIt( groups );
+ while ( *grpIt )
+ {
+ QPtrListIterator<Kopete::Group> candidate = grpIt;
+ ++grpIt;
+ bool found = false;
+ GWContactInstanceList::Iterator instIt = instances.begin();
+ for ( ; instIt != instances.end(); ++instIt )
+ {
+ QString groupId = ( *candidate )->pluginData( protocol(), accountId() + " objectId" );
+ if ( groupId.isEmpty() )
+ if ( *candidate == Kopete::Group::topLevel() )
+ groupId = "0"; // hack the top level's objectId to 0
+ else
+ continue;
+
+ GWFolder * folder = ::qt_cast<GWFolder*>( ( *instIt )->parent() );
+ if ( folder->id == ( unsigned int )groupId.toInt() )
+ {
+ found = true;
+ instances.remove( instIt );
+ break;
+ }
+ }
+ if ( !found )
+ {
+ if ( c->metaContact()->contacts().count() == 1 )
+ {
+ if ( c->metaContact()->groups().count() == 1 )
+ {
+ kdDebug( GROUPWISE_DEBUG_GLOBAL ) << "contact instance " << c->dn() << " not found on server side list, deleting metacontact with only this contact, in one group" << c->metaContact()->displayName() << endl;
+ Kopete::ContactList::self()->removeMetaContact( c->metaContact() );
+ break;
+ }
+ else
+ {
+ kdDebug( GROUPWISE_DEBUG_GLOBAL ) << "contact instance " << c->dn() << " not found, removing metacontact " << c->metaContact()->displayName() << " from group " << ( *candidate )->displayName() << endl;
+ c->metaContact()->removeFromGroup( *candidate );
+ }
+ }
+ else
+ {
+ if ( c->metaContact()->groups().count() == 1 )
+ {
+ kdDebug( GROUPWISE_DEBUG_GLOBAL ) << "contact instance " << c->dn() << " not found, removing contact " << c->metaContact()->displayName() << " from metacontact with other contacts " << endl;
+ c->deleteLater();
+ break;
+ }
+ else
+ kdDebug( GROUPWISE_DEBUG_GLOBAL ) << "metacontact " << c->metaContact()->displayName( ) << "has multiple children and group membership, and contact " << c->dn() << " was removed from one group on the server." << endl;
+ conflicts = true;
+ }
+ } //
+ } //end while, now check the next group membership
+ } //end for, now check the next groupwise contact
+ if ( conflicts )
+ // show queuedmessagebox
+ KPassivePopup::message( i18n( "Conflicting Changes Made Offline" ), i18n( "A change happened to your GroupWise contact list while you were offline which was impossible to reconcile." ), Kopete::UI::Global::mainWidget() );
+ m_dontSync = false;
+}
+
+void GroupWiseAccount::slotLoginFailed()
+{
+ kdDebug ( GROUPWISE_DEBUG_GLOBAL ) << k_funcinfo << endl;
+ password().setWrong();
+ disconnect();
+ connect();
+}
+
+void GroupWiseAccount::slotKopeteGroupRenamed( Kopete::Group * renamedGroup )
+{
+ if ( isConnected() )
+ {
+ QString objectIdString = renamedGroup->pluginData( protocol(), accountId() + " objectId" );
+ // if this group exists on the server
+ if ( !objectIdString.isEmpty() )
+ {
+ kdDebug ( GROUPWISE_DEBUG_GLOBAL ) << k_funcinfo << endl;
+
+ GroupWise::FolderItem fi;
+ fi.id = objectIdString.toInt();
+ if ( fi.id != 0 )
+ {
+ fi.sequence = renamedGroup->pluginData( protocol(), accountId() + " sequence" ).toInt();
+ fi.name= renamedGroup->pluginData( protocol(), accountId() + " serverDisplayName" );
+
+ UpdateFolderTask * uft = new UpdateFolderTask( client()->rootTask() );
+ uft->renameFolder( renamedGroup->displayName(), fi );
+ uft->go( true );
+ // would be safer to do this in a slot fired on uft's finished() signal
+ renamedGroup->setPluginData( protocol(), accountId() + " serverDisplayName",
+ renamedGroup->displayName() );
+ }
+ }
+ }
+ //else
+ // errornotconnected
+}
+
+void GroupWiseAccount::slotKopeteGroupRemoved( Kopete::Group * group )
+{
+ if ( isConnected() )
+ {
+ kdDebug ( GROUPWISE_DEBUG_GLOBAL ) << k_funcinfo << endl;
+ // the member contacts should be deleted separately, so just delete the folder here
+ // get the folder object id
+ QString objectIdString = group->pluginData( protocol(), accountId() + " objectId" );
+ if ( !objectIdString.isEmpty() )
+ {
+ kdDebug( GROUPWISE_DEBUG_GLOBAL ) << "deleting folder with objectId: " << objectIdString << endl;
+ int objectId = objectIdString.toInt();
+ if ( objectId == 0 )
+ {
+ kdDebug( GROUPWISE_DEBUG_GLOBAL ) << "deleted folder " << group->displayName() << " has root folder objectId 0!" << endl;
+ return;
+ }
+ DeleteItemTask * dit = new DeleteItemTask( client()->rootTask() );
+ dit->item( 0, objectId );
+ // the group is deleted synchronously after this slot returns; so there is no point listening for signals
+ dit->go( true );
+ }
+ }
+ //else
+ // errornotconnected
+}
+
+void GroupWiseAccount::slotConnError()
+{
+ kdDebug ( GROUPWISE_DEBUG_GLOBAL ) << k_funcinfo << endl;
+ KMessageBox::queuedMessageBox( Kopete::UI::Global::mainWidget(), KMessageBox::Sorry,
+ i18n( "Error shown when connecting failed", "Kopete was not able to connect to the GroupWise Messenger server for account '%1'.\nPlease check your server and port settings and try again." ).arg( accountId() ) , i18n ("Unable to Connect '%1'").arg( accountId() ) );
+
+ disconnect();
+}
+
+void GroupWiseAccount::slotConnConnected()
+{
+ kdDebug ( GROUPWISE_DEBUG_GLOBAL ) << k_funcinfo << endl;
+}
+
+void GroupWiseAccount::slotCSDisconnected()
+{
+ kdDebug ( GROUPWISE_DEBUG_GLOBAL ) << k_funcinfo << "Disconnected from Groupwise server." << endl;
+ myself()->setOnlineStatus( protocol()->groupwiseOffline );
+ QValueList<GroupWiseChatSession *>::ConstIterator it;
+ for ( it = m_chatSessions.begin() ; it != m_chatSessions.end(); ++it )
+ (*it)->setClosed();
+ setAllContactsStatus( protocol()->groupwiseOffline );
+ client()->close();
+}
+
+void GroupWiseAccount::slotCSConnected()
+{
+ kdDebug ( GROUPWISE_DEBUG_GLOBAL ) << k_funcinfo << "Connected to Groupwise server." << endl;
+
+}
+
+void GroupWiseAccount::slotCSError( int error )
+{
+ kdDebug ( GROUPWISE_DEBUG_GLOBAL ) << k_funcinfo << "Got error from ClientStream:" << error << endl;
+}
+
+void GroupWiseAccount::slotCSWarning( int warning )
+{
+ kdDebug ( GROUPWISE_DEBUG_GLOBAL ) << k_funcinfo << "Got warning from ClientStream:" << warning << endl;
+}
+
+void GroupWiseAccount::slotTLSHandshaken()
+{
+ kdDebug ( GROUPWISE_DEBUG_GLOBAL ) << k_funcinfo << "TLS handshake complete" << endl;
+ int validityResult = m_QCATLS->certificateValidityResult ();
+
+ if( validityResult == QCA::TLS::Valid )
+ {
+ kdDebug ( GROUPWISE_DEBUG_GLOBAL ) << "Certificate is valid, continuing." << endl;
+ // valid certificate, continue
+ m_tlsHandler->continueAfterHandshake ();
+ }
+ else
+ {
+ kdDebug ( GROUPWISE_DEBUG_GLOBAL ) << "Certificate is not valid, continuing anyway" << endl;
+ // certificate is not valid, query the user
+ if(handleTLSWarning (validityResult, server (), myself()->contactId ()) == KMessageBox::Continue)
+ {
+ m_tlsHandler->continueAfterHandshake ();
+ }
+ else
+ {
+ disconnect ( Kopete::Account::Manual );
+ }
+ }
+}
+
+int GroupWiseAccount::handleTLSWarning (int warning, QString server, QString accountId)
+{
+ QString validityString, code;
+
+ switch(warning)
+ {
+ case QCA::TLS::NoCert:
+ validityString = i18n("No certificate was presented.");
+ code = "NoCert";
+ break;
+ case QCA::TLS::HostMismatch:
+ validityString = i18n("The host name does not match the one in the certificate.");
+ code = "HostMismatch";
+ break;
+ case QCA::TLS::Rejected:
+ validityString = i18n("The Certificate Authority rejected the certificate.");
+ code = "Rejected";
+ break;
+ case QCA::TLS::Untrusted:
+ // FIXME: write better error message here
+ validityString = i18n("The certificate is untrusted.");
+ code = "Untrusted";
+ break;
+ case QCA::TLS::SignatureFailed:
+ validityString = i18n("The signature is invalid.");
+ code = "SignatureFailed";
+ break;
+ case QCA::TLS::InvalidCA:
+ validityString = i18n("The Certificate Authority is invalid.");
+ code = "InvalidCA";
+ break;
+ case QCA::TLS::InvalidPurpose:
+ // FIXME: write better error message here
+ validityString = i18n("Invalid certificate purpose.");
+ code = "InvalidPurpose";
+ break;
+ case QCA::TLS::SelfSigned:
+ validityString = i18n("The certificate is self-signed.");
+ code = "SelfSigned";
+ break;
+ case QCA::TLS::Revoked:
+ validityString = i18n("The certificate has been revoked.");
+ code = "Revoked";
+ break;
+ case QCA::TLS::PathLengthExceeded:
+ validityString = i18n("Maximum certificate chain length was exceeded.");
+ code = "PathLengthExceeded";
+ break;
+ case QCA::TLS::Expired:
+ validityString = i18n("The certificate has expired.");
+ code = "Expired";
+ break;
+ case QCA::TLS::Unknown:
+ default:
+ validityString = i18n("An unknown error occurred trying to validate the certificate.");
+ code = "Unknown";
+ break;
+ }
+
+ return KMessageBox::warningContinueCancel(Kopete::UI::Global::mainWidget (),
+ i18n("The certificate of server %1 could not be validated for account %2: %3").
+ arg(server).
+ arg(accountId).
+ arg(validityString),
+ i18n("GroupWise Connection Certificate Problem"),
+ KStdGuiItem::cont(),
+ QString("KopeteTLSWarning") + server + code);
+}
+
+void GroupWiseAccount::slotTLSReady( int secLayerCode )
+{
+ // i don't know what secLayerCode is for...
+ Q_UNUSED( secLayerCode );
+ kdDebug( GROUPWISE_DEBUG_GLOBAL ) << k_funcinfo << endl;
+ m_client->start( server(), port(), accountId(), password().cachedValue() );
+}
+
+void GroupWiseAccount::handleIncomingMessage( const ConferenceEvent & message )
+{
+ QString typeName = "UNKNOWN";
+ if ( message.type == ReceiveMessage )
+ typeName = "message";
+ else if ( message.type == ReceiveAutoReply )
+ typeName = "autoreply";
+ else if ( message.type == ReceivedBroadcast )
+ typeName = "broadcast";
+ else if ( message.type == ReceivedSystemBroadcast )
+ typeName = "system broadcast";
+
+ kdDebug( GROUPWISE_DEBUG_GLOBAL ) << k_funcinfo << " received a " << typeName << " from " << message.user << ", to conference: " << message.guid << ", message: " << message.message << endl;
+
+ GroupWiseContact * sender = contactForDN( message.user );
+ if ( !sender )
+ sender = createTemporaryContact( message.user );
+
+ // if we receive a message from an Offline contact, they are probably blocking us
+ // but we have to set their status to Unknown so that we can reply to them.
+ kdDebug( GROUPWISE_DEBUG_GLOBAL) << "sender is: " << sender->onlineStatus().description() << endl;
+ if ( sender->onlineStatus() == protocol()->groupwiseOffline ) {
+ sender->setMessageReceivedOffline( true );
+ }
+
+ Kopete::ContactPtrList contactList;
+ contactList.append( sender );
+ // FIND A MESSAGE MANAGER FOR THIS CONTACT
+ GroupWiseChatSession *sess = chatSession( contactList, message.guid, Kopete::Contact::CanCreate );
+
+ // add an auto-reply indicator if needed
+ QString messageMunged = message.message;
+ if ( message.type == ReceiveAutoReply )
+ {
+ QString prefix = i18n("Prefix used for automatically generated auto-reply"
+ " messages when the contact is Away, contains contact's name",
+ "Auto reply from %1: " ).arg( sender->metaContact()->displayName() );
+ messageMunged = prefix + message.message;
+ }
+ if ( message.type == GroupWise::ReceivedBroadcast )
+ {
+ QString prefix = i18n("Prefix used for broadcast messages",
+ "Broadcast message from %1: " ).arg( sender->metaContact()->displayName() );
+ messageMunged = prefix + message.message;
+ }
+ if ( message.type == GroupWise::ReceivedSystemBroadcast )
+ {
+ QString prefix = i18n("Prefix used for system broadcast messages",
+ "System Broadcast message from %1: " ).arg( sender->metaContact()->displayName() );
+ messageMunged = prefix + message.message;
+ }
+
+ kdDebug(GROUPWISE_DEBUG_GLOBAL) << k_funcinfo << " message before KopeteMessage and appending: " << messageMunged << endl;
+ Kopete::Message * newMessage =
+ new Kopete::Message( message.timeStamp, sender, contactList, messageMunged,
+ Kopete::Message::Inbound,
+ ( message.type == ReceiveAutoReply ) ? Kopete::Message::PlainText : Kopete::Message::RichText );
+ Q_ASSERT( sess );
+ sess->appendMessage( *newMessage );
+ kdDebug(GROUPWISE_DEBUG_GLOBAL) << "message from KopeteMessage: plainbody: " << newMessage->plainBody() << " parsedbody: " << newMessage->parsedBody() << endl;
+ delete newMessage;
+}
+
+void GroupWiseAccount::receiveFolder( const FolderItem & folder )
+{
+ kdDebug( GROUPWISE_DEBUG_GLOBAL ) << k_funcinfo
+ << " objectId: " << folder.id
+ << " sequence: " << folder.sequence
+ << " parentId: " << folder.parentId
+ << " displayName: " << folder.name << endl;
+ if ( folder.parentId != 0 )
+ {
+ kdWarning( GROUPWISE_DEBUG_GLOBAL ) << " - received a nested folder. These were not supported in GroupWise or Kopete as of Sept 2004, aborting! (parentId = " << folder.parentId << ")" << endl;
+ return;
+ }
+
+ GWFolder * fld = m_serverListModel->addFolder( folder.id, folder.sequence, folder.name );
+ Q_ASSERT( fld );
+
+ // either find a local group and record these details there, or create a new group to suit
+ Kopete::Group * found = 0;
+ QPtrList<Kopete::Group> groupList = Kopete::ContactList::self()->groups();
+ for ( Kopete::Group *grp = groupList.first(); grp; grp = groupList.next() )
+ {
+ // see if there is already a local group that matches this group
+ QString groupId = grp->pluginData( protocol(), accountId() + " objectId" );
+ if ( groupId.isEmpty() )
+ if ( folder.name == grp->displayName() ) // no match on id, match on display name instead
+ {
+ grp->setPluginData( protocol(), accountId() + " objectId", QString::number( folder.id ) );
+ found = grp;
+ break;
+ }
+ if ( folder.id == (unsigned int)groupId.toInt() )
+ {
+ // was it renamed locally while we were offline?
+ if ( grp->displayName() != folder.name )
+ {
+ slotKopeteGroupRenamed( grp );
+ grp->setPluginData( protocol(), accountId() + " serverDisplayName", grp->displayName() );
+ fld->displayName = grp->displayName();
+ }
+
+ found = grp;
+ break;
+ }
+ }
+
+ if ( !found )
+ {
+ kdDebug( GROUPWISE_DEBUG_GLOBAL ) << " - not found locally, creating Kopete::Group" << endl;
+ Kopete::Group * grp = new Kopete::Group( folder.name );
+ grp->setPluginData( protocol(), accountId() + " serverDisplayName", folder.name );
+ grp->setPluginData( protocol(), accountId() + " objectId", QString::number( folder.id ) );
+ Kopete::ContactList::self()->addGroup( grp );
+ }
+}
+
+void GroupWiseAccount::receiveContact( const ContactItem & contact )
+{
+ kdDebug( GROUPWISE_DEBUG_GLOBAL ) << k_funcinfo
+ << " objectId: " << contact.id
+ << ", sequence: " << contact.sequence
+ << ", parentId: " << contact.parentId
+ << ", dn: " << contact.dn
+ << ", displayName: " << contact.displayName << endl;
+ //kdDebug( GROUPWISE_DEBUG_GLOBAL ) << "\n dotted notation is '" << protocol()->dnToDotted( contact.dn ) << "'\n" <<endl;
+
+ // add to new style contact list
+ GWContactInstance * gwInst = m_serverListModel->addContactInstance( contact.id, contact.parentId, contact.sequence, contact.displayName, contact.dn );
+ Q_ASSERT( gwInst );
+
+ GroupWiseContact * c = contactForDN( contact.dn );
+ // this contact is new to us, create him on the server
+ if ( !c )
+ {
+ Kopete::MetaContact *metaContact = new Kopete::MetaContact();
+ metaContact->setDisplayName( contact.displayName );
+ c = new GroupWiseContact( this, contact.dn, metaContact, contact.id, contact.parentId, contact.sequence );
+ Kopete::ContactList::self()->addMetaContact( metaContact );
+ }
+ // add the metacontact to the ContactItem's group, if not there aleady
+ if ( contact.parentId == 0 )
+ c->metaContact()->addToGroup( Kopete::Group::topLevel() );
+ else
+ {
+ // check the metacontact is in the group this listing-of-the-contact is in...
+ GWFolder * folder = m_serverListModel->findFolderById( contact.parentId );
+ if ( !folder ) // inconsistent
+ {
+ kdDebug( GROUPWISE_DEBUG_GLOBAL ) << " - ERROR - contact's folder doesn't exist on server" << endl;
+ DeleteItemTask * dit = new DeleteItemTask( client()->rootTask() );
+ dit->item( contact.parentId, contact.id );
+// QObject::connect( dit, SIGNAL( gotContactDeleted( const ContactItem & ) ), SLOT( receiveContactDeleted( const ContactItem & ) ) );
+ dit->go( true );
+ return;
+ }
+ Kopete::Group *grp = Kopete::ContactList::self()->findGroup( folder->displayName );
+ // grp should exist, because we receive the folders from the server before the contacts
+ if ( grp )
+ {
+ kdDebug( GROUPWISE_DEBUG_GLOBAL ) << " - making sure MC is in group " << grp->displayName() << endl;
+ m_dontSync = true;
+ c->metaContact()->addToGroup( grp ); //addToGroup() is safe to call if already a member
+ m_dontSync = false;
+ }
+ }
+
+ c->setNickName( contact.displayName );
+ //m_serverListModel->dump();
+}
+
+void GroupWiseAccount::receiveAccountDetails( const ContactDetails & details )
+{
+ kdDebug( GROUPWISE_DEBUG_GLOBAL ) << k_funcinfo
+ << "Auth attribute: " << details.authAttribute
+ << ", Away message: " << details.awayMessage
+ << ", CN" << details.cn
+ << ", DN" << details.dn
+ << ", fullName" << details.fullName
+ << ", surname" << details.surname
+ << ", givenname" << details.givenName
+ << ", status" << details.status
+ << endl;
+ if ( details.cn.lower() == accountId().lower().section('@', 0, 0) ) // incase user set account ID [email protected]
+ {
+ kdDebug( GROUPWISE_DEBUG_GLOBAL ) << k_funcinfo << " - got our details in contact list, updating them" << endl;
+ GroupWiseContact * detailsOwner= static_cast<GroupWiseContact *>( myself() );
+ detailsOwner->updateDetails( details );
+ //detailsOwner->setProperty( Kopete::Global::Properties::self()->nickName(), details.fullName );
+
+ // Very important, without knowing our DN we can't do much else
+ Q_ASSERT( !details.dn.isEmpty() );
+ m_client->setUserDN( details.dn );
+ return;
+ }
+ else
+ {
+ kdDebug( GROUPWISE_DEBUG_GLOBAL ) << k_funcinfo << " - passed someone else's details in contact list!" << endl;
+ }
+}
+
+void GroupWiseAccount::receiveContactUserDetails( const ContactDetails & details )
+{
+ kdDebug( GROUPWISE_DEBUG_GLOBAL ) << k_funcinfo
+ << "Auth attribute: " << details.authAttribute
+ << ", Away message: " << details.awayMessage
+ << ", CN" << details.cn
+ << ", DN" << details.dn
+ << ", fullName" << details.fullName
+ << ", surname" << details.surname
+ << ", givenname" << details.givenName
+ << ", status" << details.status
+ << endl;
+ // HACK: lowercased DN
+ if ( !details.dn.isNull() )
+ {
+ // are the details for someone in our contact list?
+ GroupWiseContact * detailsOwner = contactForDN( details.dn );
+
+ if( detailsOwner )
+ {
+ kdDebug( GROUPWISE_DEBUG_GLOBAL ) << " - updating details for " << details.dn << endl;
+ detailsOwner->updateDetails( details );
+ }
+ else
+ {
+ kdDebug( GROUPWISE_DEBUG_GLOBAL ) << " - got details for " << details.dn << ", but they aren't in our contact list!" << endl;
+ }
+ }
+}
+
+GroupWiseContact * GroupWiseAccount::createTemporaryContact( const QString & dn )
+{
+ ContactDetails details = client()->userDetailsManager()->details( dn );
+ GroupWiseContact * c = static_cast<GroupWiseContact *>( contacts()[ details.dn.lower() ] );
+ if ( !c && details.dn != accountId() )
+ {
+ kdDebug( GROUPWISE_DEBUG_GLOBAL ) << "Got a temporary contact DN: " << details.dn << endl;
+ // the client is telling us about a temporary contact we need to know about so add them
+ Kopete::MetaContact *metaContact = new Kopete::MetaContact ();
+ metaContact->setTemporary (true);
+ QString displayName = details.fullName;
+ if ( displayName.isEmpty() )
+ displayName = details.givenName + " " + details.surname;
+
+ metaContact->setDisplayName( displayName );
+ c = new GroupWiseContact( this, details.dn, metaContact, 0, 0, 0 );
+ c->updateDetails( details );
+ c->setProperty( Kopete::Global::Properties::self()->nickName(), protocol()->dnToDotted( details.dn ) );
+ Kopete::ContactList::self()->addMetaContact( metaContact );
+ // the contact details probably don't contain status - but we can ask for it
+ if ( details.status == GroupWise::Invalid && isConnected() )
+ m_client->requestStatus( details.dn );
+ }
+ else
+ kdDebug( GROUPWISE_DEBUG_GLOBAL ) << "Notified of existing temporary contact DN: " << details.dn << endl;
+ return c;
+}
+
+void GroupWiseAccount::receiveStatus( const QString & contactId, Q_UINT16 status, const QString &awayMessage )
+{
+ kdDebug( GROUPWISE_DEBUG_GLOBAL ) << k_funcinfo << "got status for: " << contactId << ", status: " << status << ", away message: " << awayMessage << endl;
+ GroupWiseContact * c = contactForDN( contactId );
+ if ( c )
+ {
+ kdDebug( GROUPWISE_DEBUG_GLOBAL ) << " - their KOS is: " << protocol()->gwStatusToKOS( status ).description() << endl;
+ Kopete::OnlineStatus kos = protocol()->gwStatusToKOS( status );
+ c->setOnlineStatus( kos );
+ c->setProperty( protocol()->propAwayMessage, awayMessage );
+ }
+ else
+ kdDebug( GROUPWISE_DEBUG_GLOBAL ) << " couldn't find " << contactId << endl;
+}
+
+void GroupWiseAccount::changeOurStatus( GroupWise::Status status, const QString & awayMessage, const QString & autoReply )
+{
+ if ( status == GroupWise::Offline )
+ myself()->setOnlineStatus( protocol()->groupwiseAppearOffline );
+ else
+ myself()->setOnlineStatus( protocol()->gwStatusToKOS( status ) );
+ myself()->setProperty( protocol()->propAwayMessage, awayMessage );
+ myself()->setProperty( protocol()->propAutoReply, autoReply );
+}
+
+void GroupWiseAccount::sendMessage( const GroupWise::ConferenceGuid &guid, const Kopete::Message & message )
+{
+ kdDebug ( GROUPWISE_DEBUG_GLOBAL ) << k_funcinfo << endl;
+ // make an outgoing message
+ if ( isConnected() )
+ {
+ GroupWise::OutgoingMessage outMsg;
+ outMsg.guid = guid;
+ outMsg.message = message.plainBody();
+ outMsg.rtfMessage = protocol()->rtfizeText( message.plainBody() );
+ // make a list of DNs to send to
+ QStringList addresseeDNs;
+ Kopete::ContactPtrList addressees = message.to();
+ for ( Kopete::Contact * contact = addressees.first(); contact; contact = addressees.next() )
+ addresseeDNs.append( static_cast< GroupWiseContact* >( contact )->dn() );
+ // send the message
+ m_client->sendMessage( addresseeDNs, outMsg );
+ }
+}
+
+bool GroupWiseAccount::createContact( const QString& contactId, Kopete::MetaContact* parentContact )
+{
+ kdDebug ( GROUPWISE_DEBUG_GLOBAL ) << k_funcinfo << "contactId: " << contactId << endl;
+
+ // first find all the groups that this contact is a member of
+ // record, in a folderitem, their display names and groupwise object id
+ // Set object id to 0 if not found - they do not exist on the server
+ bool topLevel = false;
+ QValueList< FolderItem > folders;
+ Kopete::GroupList groupList = parentContact->groups();
+ for ( Kopete::Group *group = groupList.first(); group; group = groupList.next() )
+ {
+ if ( group->type() == Kopete::Group::TopLevel ) // no need to create it on the server
+ {
+ topLevel = true;
+ continue;
+ }
+
+ kdDebug( GROUPWISE_DEBUG_GLOBAL ) << "looking up: " << group->displayName() << endl;
+ GWFolder * fld = m_serverListModel->findFolderByName( group->displayName() );
+ FolderItem fi;
+ if ( fld )
+ {
+ kdDebug( GROUPWISE_DEBUG_GLOBAL ) << fld->displayName << endl;
+ //FIXME - get rid of FolderItem & co
+ fi.parentId = ::qt_cast<GWFolder*>( fld->parent() )->id;
+ fi.id = fld->id;
+ fi.name = fld->displayName;
+ }
+ else
+ {
+ kdDebug( GROUPWISE_DEBUG_GLOBAL ) << "folder: " << group->displayName() <<
+ "not found in server list model." << endl;
+ fi.parentId = 0;
+ fi.id = 0;
+ fi.name = group->displayName();
+ }
+ folders.append( fi );
+
+ }
+
+ // find out the sequence number to use for any new folders
+ int highestFreeSequence = m_serverListModel->maxSequenceNumber() + 1;
+
+ // send this list along with the contact details to the server
+ // CreateContactTask will create the missing folders on the server
+ // and then add the contact to each one
+ // finally it will signal finished(), and we can query it for the details
+ // we gave it earlier and make sure the contact was successfully created.
+ //
+ // Since ToMetaContact expects synchronous contact creation
+ // we have to create the contact optimistically.
+ GroupWiseContact * gc = new GroupWiseContact( this, contactId, parentContact, 0, 0, 0 );
+ ContactDetails dt = client()->userDetailsManager()->details( contactId );
+ QString displayAs;
+ if ( dt.fullName.isEmpty() )
+ displayAs = dt.givenName + " " + dt.surname;
+ else
+ displayAs = dt.fullName;
+
+ gc->setNickName( displayAs );
+ // If the CreateContactTask finishes with an error, we have to
+ // delete the contact we just created, in receiveContactCreated :/
+
+ if ( folders.isEmpty() && !topLevel )
+ {
+ kdDebug( GROUPWISE_DEBUG_GLOBAL ) << "aborting because we didn't find any groups to add them to" << endl;
+ return false;
+ }
+
+ // get the contact's full name to use as the display name of the created contact
+ CreateContactTask * cct = new CreateContactTask( client()->rootTask() );
+ cct->contactFromUserId( contactId, parentContact->displayName(), highestFreeSequence, folders, topLevel );
+ QObject::connect( cct, SIGNAL( finished() ), SLOT( receiveContactCreated() ) );
+ cct->go( true );
+ return true;
+}
+
+void GroupWiseAccount::receiveContactCreated()
+{
+ kdDebug( GROUPWISE_DEBUG_GLOBAL ) << k_funcinfo << endl;
+ m_serverListModel->dump();
+
+ CreateContactTask * cct = ( CreateContactTask * )sender();
+ if ( cct->success() )
+ {
+ if ( client()->userDetailsManager()->known( cct->dn() ) )
+ {
+ ContactDetails dt = client()->userDetailsManager()->details( cct->dn() );
+ GroupWiseContact * c = contactForDN( cct->dn() );
+ c->setOnlineStatus( protocol()->gwStatusToKOS( dt.status ) );
+ c->setNickName( dt.fullName );
+ c->updateDetails( dt );
+ }
+ else
+ {
+ client()->requestDetails( QStringList( cct->dn() ) );
+ client()->requestStatus( cct->dn() );
+ }
+ }
+ else
+ {
+ // delete the contact created optimistically using the supplied userid;
+ Kopete::Contact * c = contacts()[ protocol()->dnToDotted( cct->userId() ) ];
+ if ( c )
+ {
+ // if the contact creation failed because it already exists on the server, don't delete it
+ if (!cct->statusCode() == NMERR_DUPLICATE_CONTACT )
+ {
+ if ( c->metaContact()->contacts().count() == 1 )
+ Kopete::ContactList::self()->removeMetaContact( c->metaContact() );
+ else
+ delete c;
+ }
+ }
+
+ KMessageBox::queuedMessageBox( Kopete::UI::Global::mainWidget (), KMessageBox::Error,
+ i18n ("The contact %1 could not be added to the contact list, with error message: %2").
+ arg(cct->userId() ).arg( cct->statusString() ),
+ i18n ("Error Adding Contact") );
+ }
+}
+
+void GroupWiseAccount::deleteContact( GroupWiseContact * contact )
+{
+ kdDebug( GROUPWISE_DEBUG_GLOBAL ) << k_funcinfo << endl;
+ contact->setDeleting( true );
+ if ( isConnected() )
+ {
+ // remove all the instances of this contact from the server's contact list
+ GWContactInstanceList instances = m_serverListModel->instancesWithDn( contact->dn() );
+ GWContactInstanceList::iterator it = instances.begin();
+ for ( ; it != instances.end(); ++it )
+ {
+ DeleteItemTask * dit = new DeleteItemTask( client()->rootTask() );
+ dit->item( ::qt_cast<GWFolder*>( (*it)->parent() )->id, (*it)->id );
+ QObject::connect( dit, SIGNAL( gotContactDeleted( const ContactItem & ) ), SLOT( receiveContactDeleted( const ContactItem & ) ) );
+ dit->go( true );
+ }
+ }
+}
+
+void GroupWiseAccount::receiveContactDeleted( const ContactItem & instance )
+{
+ kdDebug( GROUPWISE_DEBUG_GLOBAL ) << k_funcinfo << endl;
+ // an instance of this contact was deleted on the server.
+ // Remove it from the model of the server side list,
+ // and if there are no other instances of this contact, delete the contact
+ m_serverListModel->removeInstanceById( instance.id );
+ m_serverListModel->dump();
+
+ GWContactInstanceList instances = m_serverListModel->instancesWithDn( instance.dn );
+ kdDebug( GROUPWISE_DEBUG_GLOBAL ) << " - " << instance.dn << " now has " << instances.count() << " instances remaining." << endl;
+ GroupWiseContact * c = contactForDN( instance.dn );
+ if ( c && instances.count() == 0 && c->deleting() )
+ {
+ c->deleteLater();
+ }
+}
+
+
+void GroupWiseAccount::slotConnectedElsewhere()
+{
+ KPassivePopup::message( i18n ("Signed in as %1 Elsewhere").arg( accountId() ),
+ i18n( "The parameter is the user's own account id for this protocol", "You have been disconnected from GroupWise Messenger because you signed in as %1 elsewhere" ).arg( accountId() ) , Kopete::UI::Global::mainWidget() );
+ disconnect();
+}
+
+void GroupWiseAccount::receiveInvitation( const ConferenceEvent & event )
+{
+ // ask the user if they want to accept the invitation or not
+ GroupWiseContact * contactFrom = contactForDN( event.user );
+ if ( !contactFrom )
+ contactFrom = createTemporaryContact( event.user );
+ if ( configGroup()->readEntry( "AlwaysAcceptInvitations" ) == "true" )
+ {
+ client()->joinConference( event.guid );
+ }
+ else
+ {
+ ReceiveInvitationDialog * dlg = new ReceiveInvitationDialog( this, event,
+ Kopete::UI::Global::mainWidget(), "invitedialog" );
+ dlg->show();
+ }
+
+}
+
+void GroupWiseAccount::receiveConferenceJoin( const GroupWise::ConferenceGuid & guid, const QStringList & participants, const QStringList & invitees )
+{
+ // get a new GWMM
+ Kopete::ContactPtrList others;
+ GroupWiseChatSession * sess = chatSession( others, guid, Kopete::Contact::CanCreate);
+ // find each contact and add them to the GWMM, and tell them they are in the conference
+ for ( QValueList<QString>::ConstIterator it = participants.begin(); it != participants.end(); ++it )
+ {
+ //kdDebug( GROUPWISE_DEBUG_GLOBAL ) << k_funcinfo << " adding participant " << *it << endl;
+ GroupWiseContact * c = contactForDN( *it );
+ if ( !c )
+ c = createTemporaryContact( *it );
+ sess->joined( c );
+ }
+ // add each invitee too
+ for ( QValueList<QString>::ConstIterator it = invitees.begin(); it != invitees.end(); ++it )
+ {
+ //kdDebug( GROUPWISE_DEBUG_GLOBAL ) << k_funcinfo << " adding invitee " << *it << endl;
+ GroupWiseContact * c = contactForDN( *it );
+ if ( !c )
+ c = createTemporaryContact( *it );
+ sess->addInvitee( c );
+ }
+ sess->view( true )->raise( false );
+}
+
+void GroupWiseAccount::receiveConferenceJoinNotify( const ConferenceEvent & event )
+{
+ kdDebug( GROUPWISE_DEBUG_GLOBAL ) << k_funcinfo << endl;
+ GroupWiseChatSession * sess = findChatSessionByGuid( event.guid );
+ if ( sess )
+ {
+ GroupWiseContact * c = contactForDN( event.user );
+ if ( !c )
+ c = createTemporaryContact( event.user );
+ sess->joined( c );
+ }
+ else
+ kdDebug( GROUPWISE_DEBUG_GLOBAL ) << k_funcinfo << " couldn't find a GWCS for conference: " << event.guid << endl;
+}
+
+void GroupWiseAccount::receiveConferenceLeft( const ConferenceEvent & event )
+{
+ kdDebug( GROUPWISE_DEBUG_GLOBAL ) << k_funcinfo << endl;
+ GroupWiseChatSession * sess = findChatSessionByGuid( event.guid );
+ if ( sess )
+ {
+ GroupWiseContact * c = contactForDN( event.user );
+ if ( c )
+ {
+ sess->left( c );
+ }
+ else
+ kdDebug( GROUPWISE_DEBUG_GLOBAL ) << k_funcinfo << " couldn't find a contact for DN: " << event.user << endl;
+ }
+ else
+ kdDebug( GROUPWISE_DEBUG_GLOBAL ) << k_funcinfo << " couldn't find a GWCS for conference: " << event.guid << endl;
+
+}
+
+void GroupWiseAccount::receiveInviteDeclined( const ConferenceEvent & event )
+{
+ kdDebug( GROUPWISE_DEBUG_GLOBAL ) << k_funcinfo << endl;
+ GroupWiseChatSession * sess = findChatSessionByGuid( event.guid );
+ if ( sess )
+ {
+ GroupWiseContact * c = contactForDN( event.user );
+ if ( c )
+ sess->inviteDeclined( c );
+ }
+ else
+ kdDebug( GROUPWISE_DEBUG_GLOBAL ) << k_funcinfo << " couldn't find a GWCS for conference: " << event.guid << endl;
+}
+
+void GroupWiseAccount::receiveInviteNotify( const ConferenceEvent & event )
+{
+ kdDebug( GROUPWISE_DEBUG_GLOBAL ) << k_funcinfo << endl;
+ GroupWiseChatSession * sess = findChatSessionByGuid( event.guid );
+ if ( sess )
+ {
+ GroupWiseContact * c = contactForDN( event.user );
+ if ( !c )
+ c = createTemporaryContact( event.user );
+
+ sess->addInvitee( c );
+ Kopete::Message declined = Kopete::Message( myself(), sess->members(), i18n("%1 has been invited to join this conversation.").arg( c->metaContact()->displayName() ), Kopete::Message::Internal, Kopete::Message::PlainText );
+ sess->appendMessage( declined );
+ }
+ else
+ kdDebug( GROUPWISE_DEBUG_GLOBAL ) << k_funcinfo << " couldn't find a GWCS for conference: " << event.guid << endl;
+}
+
+void GroupWiseAccount::slotLeavingConference( GroupWiseChatSession * sess )
+{
+ kdDebug( GROUPWISE_DEBUG_GLOBAL ) << k_funcinfo << "unregistering message manager:" << sess->guid()<< endl;
+ if( isConnected () )
+ m_client->leaveConference( sess->guid() );
+ m_chatSessions.remove( sess );
+ kdDebug( GROUPWISE_DEBUG_GLOBAL ) << k_funcinfo << "m_chatSessions now contains:" << m_chatSessions.count() << " managers" << endl;
+ Kopete::ContactPtrList members = sess->members();
+ for ( Kopete::Contact * contact = members.first(); contact; contact = members.next() )
+ {
+ static_cast< GroupWiseContact * >( contact )->setMessageReceivedOffline( false );
+ }
+}
+
+void GroupWiseAccount::slotSetAutoReply()
+{
+ bool ok;
+ QRegExp rx( ".*" );
+ QRegExpValidator validator( rx, this );
+ QString newAutoReply = KInputDialog::getText( i18n( "Enter Auto-Reply Message" ),
+ i18n( "Please enter an Auto-Reply message that will be shown to users who message you while Away or Busy" ), configGroup()->readEntry( "AutoReply" ),
+ &ok, Kopete::UI::Global::mainWidget(), "autoreplymessagedlg", &validator );
+ if ( ok )
+ configGroup()->writeEntry( "AutoReply", newAutoReply );
+}
+
+void GroupWiseAccount::slotTestRTFize()
+{
+/* bool ok;
+ const QString query = QString::fromLatin1("Enter a string to rtfize:");
+ QString testText = KLineEditDlg::getText( query, QString::null, &ok, Kopete::UI::Global::mainWidget() );
+ if ( ok )
+ kdDebug( GROUPWISE_DEBUG_GLOBAL ) << "Converted text is: '" << protocol()->rtfizeText( testText ) << "'" << endl;*/
+
+// bool ok;
+// const QString query = i18n("Enter a contactId:");
+// QString testText = KInputDialog::getText( query, i18n("This is a test dialog and will not be in the final product!" ), QString::null, &ok, Kopete::UI::Global::mainWidget() );
+// if ( !ok )
+// return;
+// kdDebug( GROUPWISE_DEBUG_GLOBAL ) << "Trying to add contact: '" << protocol()->rtfizeText( testText ) << "'" << endl;
+// Kopete::MetaContact *metaContact = new Kopete::MetaContact ();
+// metaContact->setDisplayName( "Test Add MC" );
+// metaContact->setTemporary (true);
+// createContact( testText, "Test Add Contact", metaContact );
+}
+
+void GroupWiseAccount::slotPrivacy()
+{
+ new GroupWisePrivacyDialog( this, Kopete::UI::Global::mainWidget(), "gwprivacydialog" );
+}
+
+void GroupWiseAccount::slotJoinChatRoom()
+{
+ new GroupWiseChatSearchDialog( this, Kopete::UI::Global::mainWidget(), "gwjoinchatdialog" );
+}
+
+bool GroupWiseAccount::isContactBlocked( const QString & dn )
+{
+ if ( isConnected() )
+ return client()->privacyManager()->isBlocked( dn );
+ else
+ return false;
+}
+
+void GroupWiseAccount::dumpManagers()
+{
+ kdDebug( GROUPWISE_DEBUG_GLOBAL ) << k_funcinfo << " for: " << accountId()
+ << " containing: " << m_chatSessions.count() << " managers " << endl;
+ QValueList<GroupWiseChatSession *>::ConstIterator it;
+ for ( it = m_chatSessions.begin() ; it != m_chatSessions.end(); ++it )
+ kdDebug( GROUPWISE_DEBUG_GLOBAL ) << "guid: " << (*it)->guid() << endl;
+}
+
+bool GroupWiseAccount::dontSync()
+{
+ return m_dontSync;
+}
+
+void GroupWiseAccount::syncContact( GroupWiseContact * contact )
+{
+ if ( dontSync() )
+ return;
+
+ if ( contact != myself() )
+ {
+ kdDebug( GROUPWISE_DEBUG_GLOBAL ) << k_funcinfo << endl;
+ if ( !isConnected() )
+ {
+ kdDebug( GROUPWISE_DEBUG_GLOBAL ) << "not connected, can't sync display name or group membership" << endl;
+ return;
+ }
+
+ // if this is a temporary contact, don't bother
+ if ( contact->metaContact()->isTemporary() )
+ return;
+
+ kdDebug( GROUPWISE_DEBUG_GLOBAL ) << " = CONTACT '" << contact->nickName() << "' IS IN " << contact->metaContact()->groups().count() << " MC GROUPS, AND HAS " << m_serverListModel->instancesWithDn( contact->dn() ).count() << " CONTACT LIST INSTANCES." << endl;
+
+ kdDebug( GROUPWISE_DEBUG_GLOBAL ) << " = LOOKING FOR NOOP GROUP MEMBERSHIPS" << endl;
+ // 1) Seek matches between CLIs and MCGs and remove from the lists without taking any action. match on objectid, parentid
+ // 2) Each remaining unmatched pair is a move, initiate and remove - need to take care to always use greatest unused sequence number - if we have to set the sequence number to the following sequence number within the folder, we may have a problem where after the first move, we have to wait for the state of the CLIs to be updated pending the completion of the first move - this would be difficult to cope with, because our current lists would be out of date, or we'd have to restart the sync - assuming the first move created a new matched CLI-MCG pair, we could do that with little cost.
+ // 3) Any remaining entries in MCG list are adds, carry out
+ // 4) Any remaining entries in CLI list are removes, carry out
+
+ // start by discovering the next free group sequence number in case we have to add any groups
+ int nextFreeSequence = m_serverListModel->maxSequenceNumber() + 1;
+ // 1)
+ // make a list of all the groups the metacontact is in
+ QPtrList<Kopete::Group> groupList = contact->metaContact()->groups();
+ // make a list of all the groups this contact is in, according to the server model
+ GWContactInstanceList instances = m_serverListModel->instancesWithDn( contact->dn() );
+
+ // seek corresponding pairs in both lists and remove
+ // ( for each group )
+ QPtrListIterator< Kopete::Group > grpIt( groupList );
+ while ( *grpIt )
+ {
+ QPtrListIterator< Kopete::Group > candidateGrp( groupList );
+ candidateGrp = grpIt;
+ ++grpIt;
+
+ GWContactInstanceList::Iterator instIt = instances.begin();
+ const GWContactInstanceList::Iterator instEnd = instances.end();
+ // ( see if a contactlist instance matches the group)
+ while ( instIt != instEnd )
+ {
+ GWContactInstanceList::Iterator candidateInst = instIt;
+ ++instIt;
+ GWFolder * folder = ::qt_cast<GWFolder *>( ( *candidateInst )->parent() );
+ kdDebug( GROUPWISE_DEBUG_GLOBAL ) << " - Looking for a match, MC grp '"
+ << ( *candidateGrp )->displayName()
+ << "', GWFolder '" << folder->displayName << "', objectId is " << folder->id << endl;
+
+ if ( ( folder->id == 0 && ( ( *candidateGrp ) == Kopete::Group::topLevel() ) )
+ || ( ( *candidateGrp )->displayName() == folder->displayName ) )
+ {
+ //this pair matches, we can remove its members from both lists )
+ kdDebug( GROUPWISE_DEBUG_GLOBAL ) << " - match! removing both entries" << endl;
+ instances.remove( candidateInst );
+ groupList.remove( *candidateGrp );
+ break;
+ }
+ }
+ }
+
+ kdDebug( GROUPWISE_DEBUG_GLOBAL ) << " = LOOKING FOR UNMATCHED PAIRS => GROUP MOVES" << endl;
+ grpIt.toFirst();
+ // ( take the first pair and carry out a move )
+ while ( *grpIt && !instances.isEmpty() )
+ {
+ QPtrListIterator< Kopete::Group > candidateGrp( groupList );
+ candidateGrp = grpIt;
+ ++grpIt;
+ GWContactInstanceList::Iterator instIt = instances.begin();
+ GWFolder * sourceFolder =::qt_cast<GWFolder*>( ( *instIt)->parent() );
+ kdDebug( GROUPWISE_DEBUG_GLOBAL ) << " - moving contact instance from group '" << sourceFolder->displayName << "' to group '" << ( *candidateGrp )->displayName() << "'" << endl;
+
+ // create contactItem parameter
+ ContactItem instance;
+ instance.id = ( *instIt )->id;
+ instance.parentId = sourceFolder->id;
+ instance.sequence = ( *instIt )->sequence;
+ instance.dn = ( *instIt )->dn;
+ instance.displayName = contact->nickName();
+ // identify the destination folder
+ GWFolder * destinationFolder = m_serverListModel->findFolderByName( ( ( *candidateGrp )->displayName() ) );
+ if ( destinationFolder ) // folder already exists on the server
+ {
+ MoveContactTask * mit = new MoveContactTask( client()->rootTask() );
+ mit->moveContact( instance, destinationFolder->id );
+ QObject::connect( mit, SIGNAL( gotContactDeleted( const ContactItem & ) ), SLOT( receiveContactDeleted( const ContactItem & ) ) );
+ mit->go();
+ }
+ else if ( *candidateGrp == Kopete::Group::topLevel() )
+ {
+ MoveContactTask * mit = new MoveContactTask( client()->rootTask() );
+ mit->moveContact( instance, 0 );
+ QObject::connect( mit, SIGNAL( gotContactDeleted( const ContactItem & ) ), SLOT( receiveContactDeleted( const ContactItem & ) ) );
+ mit->go();
+ }
+ else
+ {
+ MoveContactTask * mit = new MoveContactTask( client()->rootTask() );
+ QObject::connect( mit, SIGNAL( gotContactDeleted( const ContactItem & ) ),
+ SLOT( receiveContactDeleted( const ContactItem & ) ) );
+ // discover the next free sequence number and add the group using that
+ mit->moveContactToNewFolder( instance, nextFreeSequence++,
+ ( *candidateGrp )->displayName() );
+ mit->go( true );
+ }
+ groupList.remove( candidateGrp );
+ instances.remove( instIt );
+ }
+
+ kdDebug( GROUPWISE_DEBUG_GLOBAL ) << " = LOOKING FOR ADDS" << endl;
+ grpIt.toFirst();
+ while ( *grpIt )
+ {
+ QPtrListIterator< Kopete::Group > candidateGrp( groupList );
+ candidateGrp = grpIt;
+ ++grpIt;
+ GWFolder * destinationFolder = m_serverListModel->findFolderByName( ( ( *candidateGrp )->displayName() ) );
+ CreateContactInstanceTask * ccit = new CreateContactInstanceTask( client()->rootTask() );
+
+ contact->setNickName( contact->metaContact()->displayName() );
+ // does this group exist on the server? Create the contact appropriately
+ if ( destinationFolder )
+ {
+ int parentId = destinationFolder->id;
+ ccit->contactFromUserId( contact->dn(), contact->metaContact()->displayName(), parentId );
+ }
+ else
+ {
+ if ( ( *candidateGrp ) == Kopete::Group::topLevel() )
+ ccit->contactFromUserId( contact->dn(), contact->metaContact()->displayName(),
+ m_serverListModel->rootFolder->id );
+ else
+ // discover the next free sequence number and add the group using that
+ ccit->contactFromUserIdAndFolder( contact->dn(), contact->metaContact()->displayName(),
+ nextFreeSequence++, ( *candidateGrp )->displayName() );
+ }
+ ccit->go( true );
+ groupList.remove( candidateGrp );
+ }
+
+ kdDebug( GROUPWISE_DEBUG_GLOBAL ) << " = LOOKING FOR REMOVES" << endl;
+ GWContactInstanceList::Iterator instIt = instances.begin();
+ const GWContactInstanceList::Iterator instEnd = instances.end();
+ // ( remove each remaining contactlist instance, because it doesn't exist locally any more )
+ while ( instIt != instEnd )
+ {
+ GWContactInstanceList::Iterator candidateInst = instIt;
+ ++instIt;
+ GWFolder * folder =::qt_cast<GWFolder*>( ( *candidateInst )->parent() );
+ kdDebug( GROUPWISE_DEBUG_GLOBAL ) << " - remove contact instance '"<< ( *candidateInst )->id << "' in group '" << folder->displayName << "'" << endl;
+
+ DeleteItemTask * dit = new DeleteItemTask( client()->rootTask() );
+ dit->item( folder->id, (*candidateInst)->id );
+ QObject::connect( dit, SIGNAL( gotContactDeleted( const ContactItem & ) ), SLOT( receiveContactDeleted( const ContactItem & ) ) );
+ dit->go( true );
+
+ instances.remove( candidateInst );
+ }
+
+ // start an UpdateItem
+ if ( contact->metaContact()->displayName() != contact->nickName() )
+ {
+ kdDebug( GROUPWISE_DEBUG_GLOBAL ) << " updating the contact's display name to the metacontact's: " << contact->metaContact()->displayName() << endl;
+ // form a list of the contact's groups
+ GWContactInstanceList instances = m_serverListModel->instancesWithDn( contact->dn() );
+ GWContactInstanceList::Iterator it = instances.begin();
+ const GWContactInstanceList::Iterator end = instances.end();
+ for ( ; it != end; ++it )
+ {
+ QValueList< ContactItem > instancesToChange;
+ ContactItem instance;
+ instance.id = (*it)->id;
+ instance.parentId = ::qt_cast<GWFolder *>( (*it)->parent() )->id;
+ instance.sequence = (*it)->sequence;
+ instance.dn = contact->dn();
+ instance.displayName = contact->nickName();
+ instancesToChange.append( instance );
+
+ UpdateContactTask * uct = new UpdateContactTask( client()->rootTask() );
+ uct->renameContact( contact->metaContact()->displayName(), instancesToChange );
+ QObject::connect ( uct, SIGNAL( finished() ), contact, SLOT( renamedOnServer() ) );
+ uct->go( true );
+ }
+ }
+ }
+}
+
+#include "gwaccount.moc"
diff --git a/kopete/protocols/groupwise/gwaccount.h b/kopete/protocols/groupwise/gwaccount.h
new file mode 100644
index 00000000..2e8f8348
--- /dev/null
+++ b/kopete/protocols/groupwise/gwaccount.h
@@ -0,0 +1,360 @@
+/*
+ gwaccount.h - Kopete GroupWise Protocol
+
+ Copyright (c) 2004 SUSE Linux AG http://www.suse.com
+
+ Based on Testbed
+ Copyright (c) 2003 by Will Stephenson <[email protected]>
+
+ Kopete (c) 2002-2003 by the Kopete developers <[email protected]>
+
+ *************************************************************************
+ * *
+ * This library 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 GW_ACCOUNT_H
+#define GW_ACCOUNT_H
+
+#include <kaction.h>
+
+#include <kopetechatsessionmanager.h>
+
+#include "gwerror.h"
+
+#include <managedconnectionaccount.h>
+
+class KActionMenu;
+
+namespace Kopete {
+ class Contact;
+ class Group;
+ class MetaContact;
+}
+
+class GroupWiseContact;
+class GroupWiseChatSession;
+class GroupWiseProtocol;
+class KNetworkConnector;
+namespace QCA {
+ class TLS;
+}
+class QCATLSHandler;
+class ClientStream;
+class Client;
+class GWContactList;
+
+using namespace GroupWise;
+
+/**
+ * This represents an account on a Novell GroupWise Messenger Server
+ */
+class GroupWiseAccount : public Kopete::ManagedConnectionAccount
+{
+ Q_OBJECT
+public:
+ GroupWiseAccount( GroupWiseProtocol *parent, const QString& accountID, const char *name = 0 );
+ ~GroupWiseAccount();
+
+ /**
+ * Construct the context menu used for the status bar icon
+ */
+ virtual KActionMenu* actionMenu();
+
+ // DEBUG ONLY
+ void dumpManagers();
+ // DEBUG ONLY
+ /**
+ * Creates a protocol specific Kopete::Contact subclass and adds it to the supplied
+ * Kopete::MetaContact
+ */
+ virtual bool createContact(const QString& contactId, Kopete::MetaContact* parentContact);
+ /**
+ * Delete a contact on the server
+ */
+ void deleteContact( GroupWiseContact * contact );
+ /**
+ * Called when Kopete is set globally away
+ */
+ virtual void setAway(bool away, const QString& reason);
+ /**
+ * Utility access to the port given by the user
+ */
+ int port() const;
+ /**
+ * Utility access to the server given by the user
+ */
+ const QString server() const;
+ /**
+ * Utility access to our protocol
+ */
+ GroupWiseProtocol * protocol() const;
+ /**
+ * Utility access to the @ref Client which is the main interface exposed by libgroupwise.
+ * Most protocol actions are carried out using the client's member functions but the possibility exists
+ * to start Tasks directly on the client and respond directly to their signals.
+ */
+ Client * client() const;
+ /**
+ * Utility to create or access a message manager instance for a given GUID and set of contacts
+ */
+ GroupWiseChatSession * chatSession( Kopete::ContactPtrList others, const ConferenceGuid & guid, Kopete::Contact::CanCreateFlags canCreate );
+ /**
+ * Look up a contact given a DN
+ * Returns 0 if none found
+ */
+ GroupWiseContact * contactForDN( const QString & dn );
+ /**
+ * Create a conference (start a chat) on the server
+ */
+ void createConference( const int clientId, const QStringList& invitees );
+
+ /**
+ * Send a message
+ */
+ void sendMessage( const ConferenceGuid & guid, const Kopete::Message & message );
+
+ /**
+ * Invite someone to join a conference
+ */
+ void sendInvitation( const ConferenceGuid & guid, const QString & dn, const QString & message );
+
+ /**
+ * Check a contact's blocking status
+ * Only works when connected - otherwise always returns false
+ */
+ bool isContactBlocked( const QString & m_dn );
+ /**
+ * Set up a temporary contact (not on our contact list but is messaging us or involved in a conversation that we have been invited to.
+ */
+ GroupWiseContact * createTemporaryContact( const QString & dn );
+
+ /**
+ * Check whether sync is not currently needed
+ */
+ bool dontSync();
+
+ void syncContact( GroupWiseContact * contact );
+
+public slots:
+
+ void slotTestRTFize();
+
+ /* Connects to the server. */
+ void performConnectWithPassword ( const QString &password );
+
+ /* Disconnects from the server. */
+ virtual void disconnect();
+ virtual void disconnect( Kopete::Account::DisconnectReason reason );
+
+ /** Set the online status for the account. Reimplemented from Kopete::Account */
+ void setOnlineStatus( const Kopete::OnlineStatus& status , const QString &reason = QString::null);
+signals:
+ void conferenceCreated( const int mmId, const GroupWise::ConferenceGuid & guid );
+ void conferenceCreationFailed( const int mmId, const int statusCode );
+ void contactTyping( const ConferenceEvent & );
+ void contactNotTyping( const ConferenceEvent & );
+ void privacyChanged( const QString & dn, bool allowed );
+
+
+protected slots:
+ void slotMessageSendingFailed();
+ /**
+ * Set an auto reply message for use when the account is away
+ * TODO: Extend Kopete::AwayAction so you can set multiple ones there.
+ */
+ void slotSetAutoReply();
+ /**
+ * Manage the user's privacy settings
+ */
+ void slotPrivacy();
+
+ /**
+ * Show a dialog to join a chatroom without first adding it to the contact list
+ */
+ void slotJoinChatRoom();
+
+ /**
+ * Slot informing GroupWise when a group is renamed
+ */
+ void slotKopeteGroupRenamed( Kopete::Group * );
+ /**
+ * Slot informing GroupWise when a group is removed
+ */
+ void slotKopeteGroupRemoved( Kopete::Group * );
+
+ // SERVER SIDE CONTACT LIST PROCESSING
+ /**
+ * Called when we receive a FOLDER from the server side contact list
+ * Adds to the Kopete contact list if not already present.
+ */
+ void receiveFolder( const FolderItem & folder );
+ /**
+ * Called when we receive a CONTACT from the server side contact list
+ * Adds to a folder in the Kopete contact list.
+ */
+ void receiveContact( const ContactItem & );
+ /**
+ * Called when we receive a CONTACT'S METADATA (including initial status) from the server side contact list,
+ * or in response to an explicity query. This is necessary to handle some events from the server.
+ * These events are queued in the account until the data arrives and then we handle the event.
+ */
+ void receiveContactUserDetails( const GroupWise::ContactDetails & );
+ /**
+ * Called after we create a contact on the server
+ */
+ void receiveContactCreated();
+ /**
+ * Handles the response to deleting a contact on the server
+ */
+ void receiveContactDeleted( const ContactItem & instance );
+
+ // SLOTS HANDLING PROTOCOL EVENTS
+ /**
+ * Received a message from the server.
+ * Find the conversation that this message belongs to, and display it there.
+ * @param event contains event type, sender, content, flags. Type is used to handle autoreplies, normal messages, and [system] broadcasts.
+ */
+ void handleIncomingMessage( const ConferenceEvent & );
+ /**
+ * A contact changed status
+ */
+ void receiveStatus( const QString &, Q_UINT16, const QString & );
+ /**
+ * Our status changed on the server
+ */
+ void changeOurStatus( GroupWise::Status, const QString &, const QString & );
+ /**
+ * Called when we've been disconnected for logging in as this user somewhere else
+ */
+ void slotConnectedElsewhere();
+ /**
+ * Called when we've logged in successfully
+ */
+ void slotLoggedIn();
+ /**
+ * Called when a login attempt failed
+ */
+ void slotLoginFailed();
+ /**
+ * We joined a conference having accepted an invitation, create a message manager
+ */
+ void receiveConferenceJoin( const GroupWise::ConferenceGuid & guid, const QStringList & participants, const QStringList & invitees );
+ /**
+ * Someone joined a conference, add them to the appropriate message manager
+ */
+ void receiveConferenceJoinNotify( const ConferenceEvent & );
+ /**
+ * Someone left a conference, remove them from the message manager
+ */
+ void receiveConferenceLeft( const ConferenceEvent & );
+ /**
+ * The user was invited to join a conference
+ */
+ void receiveInvitation( const ConferenceEvent & );
+ /**
+ * Notification that a third party was invited to join conference
+ */
+ void receiveInviteNotify( const ConferenceEvent & );
+ /**
+ * Notification that a third party declined an invitation
+ */
+ void receiveInviteDeclined( const ConferenceEvent & );
+ /**
+ * A conference was closed by the server because everyone has left or declined invitations
+ * Prevents any further messages to this conference
+ */
+// void closeConference();
+ // SLOTS HANDLING NETWORK EVENTS
+ /**
+ * Update the local user's metadata
+ */
+ void receiveAccountDetails( const GroupWise::ContactDetails & details );
+ /**
+ * The TLS handshake has happened, check the result
+ */
+ void slotTLSHandshaken();
+ /** The connection is ready for a login */
+ void slotTLSReady( int secLayerCode );
+ /**
+ * Called when the clientstream is connected, debug only
+ */
+ void slotCSConnected();
+ /**
+ * Performs necessary actions when the client stream has been disconnected
+ */
+ void slotCSDisconnected();
+ void slotCSError( int error );
+ void slotCSWarning( int warning );
+
+ // HOUSEKEEPING
+ /**
+ * We listen for the destroyed() signal and leave any conferences we
+ * might have been in, and remove it from our map.
+ */
+ void slotLeavingConference( GroupWiseChatSession * );
+
+ /** Debug slots */
+ void slotConnError();
+ void slotConnConnected();
+protected:
+ /**
+ * Sends a status message to the server - called by the status specific slotGoAway etc
+ */
+ //void setStatus( GroupWise::Status status, const QString & reason = QString::null );
+
+ int handleTLSWarning (int warning, QString server, QString accountId);
+
+ GroupWiseChatSession * findChatSessionByGuid( const GroupWise::ConferenceGuid & guid );
+ /**
+ * reconcile any changes to the contact list which happened offline
+ */
+ void reconcileOfflineChanges();
+ /**
+ * Memory management
+ */
+ void cleanup();
+private:
+ // action menu and its actions
+ KActionMenu * m_actionMenu;
+ KAction * m_actionAutoReply;
+ KAction * m_actionManagePrivacy;
+ KAction * m_actionJoinChatRoom;
+ // Network code
+ KNetworkConnector * m_connector;
+ QCA::TLS * m_QCATLS;
+ QCATLSHandler * m_tlsHandler;
+ ClientStream * m_clientStream;
+ // Client, entry point of libgroupwise
+ Client * m_client;
+
+ QString m_initialReason;
+ QValueList<GroupWiseChatSession*> m_chatSessions;
+ bool m_dontSync;
+ GWContactList * m_serverListModel;
+};
+
+/**
+ * @internal
+ * An action that selects an OnlineStatus and provides a status message, but not using Kopete::Away, because the status message relates only to this status.
+ */
+/*class OnlineStatusMessageAction : public KAction
+{
+ Q_OBJECT
+ public:
+ OnlineStatusMessageAction ( const Kopete::OnlineStatus& status, const QString &text, const QString &message, const QIconSet &pix, QObject *parent=0, const char *name=0);
+ signals:
+ void activated( const Kopete::OnlineStatus& status, const QString & );
+ private slots:
+ void slotActivated();
+ private:
+ Kopete::OnlineStatus m_status;
+ QString m_message;
+};
+*/
+#endif
diff --git a/kopete/protocols/groupwise/gwaddui.ui b/kopete/protocols/groupwise/gwaddui.ui
new file mode 100644
index 00000000..97bdd3b4
--- /dev/null
+++ b/kopete/protocols/groupwise/gwaddui.ui
@@ -0,0 +1,113 @@
+<!DOCTYPE UI><UI version="3.2" stdsetdef="1">
+<class>GroupWiseAddUI</class>
+<widget class="QDialog">
+ <property name="name">
+ <cstring>GroupWiseAddUI</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>406</width>
+ <height>343</height>
+ </rect>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout2</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel1</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Account name:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>m_uniqueName</cstring>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>The account name of the account you would like to add.</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>The account name of the account you would like to add.</string>
+ </property>
+ </widget>
+ <widget class="QLineEdit">
+ <property name="name">
+ <cstring>m_uniqueName</cstring>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>The account name of the account you would like to add.</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>The account name of the account you would like to add.</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QButtonGroup">
+ <property name="name">
+ <cstring>buttonGroup1</cstring>
+ </property>
+ <property name="title">
+ <string>Contact Type</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QRadioButton">
+ <property name="name">
+ <cstring>m_rbEcho</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Echo</string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Hey look! Only one option. Could you please make this a dropdown and add Null?</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Hey look! Only one option. Could you please make this a dropdown and add Null?</string>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer2</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>100</height>
+ </size>
+ </property>
+ </spacer>
+ </vbox>
+</widget>
+<layoutdefaults spacing="6" margin="11"/>
+</UI>
diff --git a/kopete/protocols/groupwise/gwbytestream.cpp b/kopete/protocols/groupwise/gwbytestream.cpp
new file mode 100644
index 00000000..cd476070
--- /dev/null
+++ b/kopete/protocols/groupwise/gwbytestream.cpp
@@ -0,0 +1,156 @@
+
+/***************************************************************************
+ gwbytestream.cpp - Byte Stream using KNetwork sockets
+ -------------------
+ begin : Wed Jul 7 2004
+ copyright : (C) 2004 by Till Gerken <[email protected]>
+
+ Kopete (C) 2004 Kopete developers <[email protected]>
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#include <qobject.h>
+#include <kbufferedsocket.h>
+#include <kdebug.h>
+#include <kresolver.h>
+
+#include "gwbytestream.h"
+#include "gwerror.h"
+
+KNetworkByteStream::KNetworkByteStream ( QObject *parent, const char */*name*/ )
+ : ByteStream ( parent )
+{
+ kdDebug ( GROUPWISE_DEBUG_GLOBAL ) << k_funcinfo << "Instantiating new KNetwork byte stream." << endl;
+
+ // reset close tracking flag
+ mClosing = false;
+
+ mSocket = new KNetwork::KBufferedSocket;
+
+ // make sure we get a signal whenever there's data to be read
+ mSocket->enableRead ( true );
+
+ // connect signals and slots
+ QObject::connect ( mSocket, SIGNAL ( gotError ( int ) ), this, SLOT ( slotError ( int ) ) );
+ QObject::connect ( mSocket, SIGNAL ( connected ( const KResolverEntry& ) ), this, SLOT ( slotConnected () ) );
+ QObject::connect ( mSocket, SIGNAL ( closed () ), this, SLOT ( slotConnectionClosed () ) );
+ QObject::connect ( mSocket, SIGNAL ( readyRead () ), this, SLOT ( slotReadyRead () ) );
+ QObject::connect ( mSocket, SIGNAL ( bytesWritten ( int ) ), this, SLOT ( slotBytesWritten ( int ) ) );
+
+}
+
+bool KNetworkByteStream::connect ( QString host, QString service )
+{
+ kdDebug ( GROUPWISE_DEBUG_GLOBAL ) << k_funcinfo << "Connecting to " << host << ", service " << service << endl;
+
+ return socket()->connect ( host, service );
+
+}
+
+bool KNetworkByteStream::isOpen () const
+{
+
+ // determine if socket is open
+ return socket()->isOpen ();
+
+}
+
+void KNetworkByteStream::close ()
+{
+ kdDebug ( GROUPWISE_DEBUG_GLOBAL ) << k_funcinfo << "Closing stream." << endl;
+
+ // close the socket and set flag that we are closing it ourselves
+ mClosing = true;
+ socket()->close();
+
+}
+
+int KNetworkByteStream::tryWrite ()
+{
+
+ // send all data from the buffers to the socket
+ QByteArray writeData = takeWrite();
+ socket()->writeBlock ( writeData.data (), writeData.size () );
+
+ return writeData.size ();
+
+}
+
+KNetwork::KBufferedSocket *KNetworkByteStream::socket () const
+{
+
+ return mSocket;
+
+}
+
+KNetworkByteStream::~KNetworkByteStream ()
+{
+
+ delete mSocket;
+
+}
+
+void KNetworkByteStream::slotConnected ()
+{
+
+ emit connected ();
+
+}
+
+void KNetworkByteStream::slotConnectionClosed ()
+{
+ kdDebug ( GROUPWISE_DEBUG_GLOBAL ) << k_funcinfo << "Socket has been closed." << endl;
+
+ // depending on who closed the socket, emit different signals
+ if ( mClosing )
+ {
+ kdDebug ( GROUPWISE_DEBUG_GLOBAL ) << "..by ourselves!" << endl;
+ kdDebug( GROUPWISE_DEBUG_GLOBAL ) << "socket error is \"" << socket()->errorString( socket()->error() ) << "\"" << endl;
+ emit connectionClosed ();
+ }
+ else
+ {
+ kdDebug ( GROUPWISE_DEBUG_GLOBAL ) << "..by the other end" << endl;
+ emit delayedCloseFinished ();
+ }
+
+}
+
+void KNetworkByteStream::slotReadyRead ()
+{
+
+ // stuff all available data into our buffers
+ QByteArray readBuffer ( socket()->bytesAvailable () );
+
+ socket()->readBlock ( readBuffer.data (), readBuffer.size () );
+
+ appendRead ( readBuffer );
+
+ emit readyRead ();
+
+}
+
+void KNetworkByteStream::slotBytesWritten ( int bytes )
+{
+
+ emit bytesWritten ( bytes );
+
+}
+
+void KNetworkByteStream::slotError ( int code )
+{
+ kdDebug ( GROUPWISE_DEBUG_GLOBAL ) << k_funcinfo << "Socket error " << code << endl;
+
+ emit error ( code );
+
+}
+
+#include "gwbytestream.moc"
diff --git a/kopete/protocols/groupwise/gwbytestream.h b/kopete/protocols/groupwise/gwbytestream.h
new file mode 100644
index 00000000..9422f9c3
--- /dev/null
+++ b/kopete/protocols/groupwise/gwbytestream.h
@@ -0,0 +1,69 @@
+
+/***************************************************************************
+ gwbytestream.h - Byte Stream using KNetwork sockets
+ adapted from jabberbytestream.h
+ -------------------
+ begin : Wed Jul 7 2004
+ copyright : (C) 2004 by Till Gerken <[email protected]>
+
+ Kopete (C) 2004 Kopete developers <[email protected]>
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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 KNETWORKBYTESTREAM_H
+#define KNETWORKBYTESTREAM_H
+
+#include <kbufferedsocket.h>
+
+#include "bytestream.h"
+
+
+/**
+ * Low level socket class, using KDE's KNetwork socket classes
+ * @author Till Gerken
+ */
+
+class KNetworkByteStream : public ByteStream
+{
+
+Q_OBJECT
+
+public:
+ KNetworkByteStream ( QObject *parent = 0, const char *name = 0 );
+
+ ~KNetworkByteStream ();
+
+ bool connect ( QString host, QString service );
+ virtual bool isOpen () const;
+ virtual void close ();
+
+ KNetwork::KBufferedSocket *socket () const;
+
+signals:
+ void connected ();
+
+protected:
+ virtual int tryWrite ();
+
+private slots:
+ void slotConnected ();
+ void slotConnectionClosed ();
+ void slotReadyRead ();
+ void slotBytesWritten ( int );
+ void slotError ( int );
+
+private:
+ KNetwork::KBufferedSocket *mSocket;
+ bool mClosing;
+
+};
+
+#endif
diff --git a/kopete/protocols/groupwise/gwchatui.rc b/kopete/protocols/groupwise/gwchatui.rc
new file mode 100644
index 00000000..5871ae50
--- /dev/null
+++ b/kopete/protocols/groupwise/gwchatui.rc
@@ -0,0 +1,17 @@
+<!DOCTYPE kpartgui>
+<kpartgui version="1" name="kopete_groupwise_chat">
+ <MenuBar>
+ <Menu noMerge="1" name="file">
+ <text>&amp;Chat</text>
+ <Action name="gwInvite" />
+ </Menu>
+ </MenuBar>
+
+
+ <ToolBar name="statusToolBar">
+ <Action name="gwSecureChat" />
+ <Action name="gwLoggingChat" />
+ </ToolBar>
+
+
+</kpartgui>
diff --git a/kopete/protocols/groupwise/gwconnector.cpp b/kopete/protocols/groupwise/gwconnector.cpp
new file mode 100644
index 00000000..c145ddfe
--- /dev/null
+++ b/kopete/protocols/groupwise/gwconnector.cpp
@@ -0,0 +1,129 @@
+
+/***************************************************************************
+ gwconnector.cpp - Socket Connector for KNetwork
+ -------------------
+ begin : Wed Jul 7 2004
+ copyright : (C) 2004 by Till Gerken <[email protected]>
+
+ Kopete (C) 2004 Kopete developers <[email protected]>
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program 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. *
+ * *
+ ***************************************************************************/
+
+#include <kbufferedsocket.h>
+#include <kdebug.h>
+#include <kresolver.h>
+
+#include "gwconnector.h"
+#include "gwerror.h"
+#include "gwbytestream.h"
+
+KNetworkConnector::KNetworkConnector ( QObject *parent, const char */*name*/ )
+ : Connector ( parent )
+{
+ kdDebug ( GROUPWISE_DEBUG_GLOBAL ) << k_funcinfo << "New KNetwork connector." << endl;
+
+ mErrorCode = KNetwork::KSocketBase::NoError;
+
+ mByteStream = new KNetworkByteStream ( this );
+
+ connect ( mByteStream, SIGNAL ( connected () ), this, SLOT ( slotConnected () ) );
+ connect ( mByteStream, SIGNAL ( error ( int ) ), this, SLOT ( slotError ( int ) ) );
+ mPort = 0;
+}
+
+KNetworkConnector::~KNetworkConnector ()
+{
+
+ delete mByteStream;
+
+}
+
+void KNetworkConnector::connectToServer ( const QString &server )
+{
+ kdDebug ( GROUPWISE_DEBUG_GLOBAL ) << k_funcinfo << "Initiating connection to " << mHost << endl;
+ Q_ASSERT( !mHost.isNull() );
+ Q_ASSERT( mPort );
+ /*
+ * FIXME: we should use a SRV lookup to determine the
+ * actual server to connect to. As this is currently
+ * not supported yet, we're using setOptHostPort().
+ * For XMPP 1.0, we need to enable this!
+ */
+
+ mErrorCode = KNetwork::KSocketBase::NoError;
+
+ if ( !mByteStream->connect ( mHost, QString::number ( mPort ) ) )
+ {
+ // Houston, we have a problem
+ mErrorCode = mByteStream->socket()->error ();
+ emit error ();
+ }
+
+}
+
+void KNetworkConnector::slotConnected ()
+{
+ kdDebug( GROUPWISE_DEBUG_GLOBAL ) << k_funcinfo << "We are connected." << endl;
+
+ // FIXME: setPeerAddress() is something different, find out correct usage later
+ //KInetSocketAddress inetAddress = mStreamSocket->address().asInet().makeIPv6 ();
+ //setPeerAddress ( QHostAddress ( inetAddress.ipAddress().addr () ), inetAddress.port () );
+
+ emit connected ();
+
+}
+
+void KNetworkConnector::slotError ( int code )
+{
+ kdDebug( GROUPWISE_DEBUG_GLOBAL ) << k_funcinfo << "Error detected: " << code << endl;
+
+ mErrorCode = code;
+ emit error ();
+}
+
+int KNetworkConnector::errorCode ()
+{
+
+ return mErrorCode;
+
+}
+
+ByteStream *KNetworkConnector::stream () const
+{
+
+ return mByteStream;
+
+}
+
+void KNetworkConnector::done ()
+{
+ kdDebug ( GROUPWISE_DEBUG_GLOBAL ) << k_funcinfo << endl;
+ mByteStream->close ();
+}
+
+void KNetworkConnector::setOptHostPort ( const QString &host, Q_UINT16 port )
+{
+ kdDebug ( GROUPWISE_DEBUG_GLOBAL ) << k_funcinfo << "Manually specifying host " << host << " and port " << port << endl;
+
+ mHost = host;
+ mPort = port;
+
+}
+
+void KNetworkConnector::setOptSSL ( bool ssl )
+{
+ kdDebug ( GROUPWISE_DEBUG_GLOBAL ) << k_funcinfo << "Setting SSL to " << ssl << endl;
+
+ setUseSSL ( ssl );
+
+}
+
+#include "gwconnector.moc"
diff --git a/kopete/protocols/groupwise/gwconnector.h b/kopete/protocols/groupwise/gwconnector.h
new file mode 100644
index 00000000..12dc59d2
--- /dev/null
+++ b/kopete/protocols/groupwise/gwconnector.h
@@ -0,0 +1,66 @@
+
+/***************************************************************************
+ gwconnector.h - Socket Connector for KNetwork
+ -------------------
+ begin : Wed Jul 7 2004
+ copyright : (C) 2004 by Till Gerken <[email protected]>
+
+ Kopete (C) 2004 Kopete developers <[email protected]>
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program 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. *
+ * *
+ ***************************************************************************/
+
+#ifndef GWCONNECTOR_H
+#define GWCONNECTOR_H
+
+#include "gwbytestream.h"
+
+#include "connector.h"
+
+class ByteStream;
+class KNetworkByteStream;
+class KResolverEntry;
+
+/**
+@author Till Gerken
+*/
+class KNetworkConnector : public Connector
+{
+
+Q_OBJECT
+
+public:
+ KNetworkConnector ( QObject *parent = 0, const char *name = 0 );
+
+ virtual ~KNetworkConnector ();
+
+ virtual void connectToServer ( const QString &server );
+ virtual ByteStream *stream () const;
+ virtual void done ();
+
+ void setOptHostPort ( const QString &host, Q_UINT16 port );
+ void setOptSSL ( bool );
+
+ int errorCode ();
+
+private slots:
+ void slotConnected ();
+ void slotError ( int );
+
+private:
+ QString mHost;
+ Q_UINT16 mPort;
+ int mErrorCode;
+
+ KNetworkByteStream *mByteStream;
+
+};
+
+#endif
diff --git a/kopete/protocols/groupwise/gwcontact.cpp b/kopete/protocols/groupwise/gwcontact.cpp
new file mode 100644
index 00000000..6dbb8c1b
--- /dev/null
+++ b/kopete/protocols/groupwise/gwcontact.cpp
@@ -0,0 +1,317 @@
+/*
+ gwcontact.cpp - Kopete GroupWise Protocol
+
+ Copyright (c) 2004 SUSE Linux AG http://www.suse.com
+
+ Based on Testbed
+ Copyright (c) 2003 by Will Stephenson <[email protected]>
+
+ Blocking status taken from MSN
+ Copyright (c) 2003 by Will Stephenson <[email protected]>
+ Copyright (c) 2002 by Duncan Mac-Vicar Prett <[email protected]>
+ Copyright (c) 2002 by Ryan Cumming <[email protected]>
+ Copyright (c) 2002-2003 by Martijn Klingens <[email protected]>
+ Copyright (c) 2002-2004 by Olivier Goffart <ogoffart @ kde.org>
+
+ Kopete (c) 2002-2003 by the Kopete developers <[email protected]>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include <qmap.h>
+
+#include <kaction.h>
+#include <kdebug.h>
+#include <klocale.h>
+
+#include <kopetemetacontact.h>
+#include <kopeteuiglobal.h>
+
+#include "client.h"
+#include "gwaccount.h"
+#include "gwprotocol.h"
+#include "privacymanager.h"
+#include "userdetailsmanager.h"
+#include "tasks/updatecontacttask.h"
+#include "ui/gwcontactproperties.h"
+
+#include "gwcontact.h"
+
+using namespace GroupWise;
+
+GroupWiseContact::GroupWiseContact( Kopete::Account* account, const QString &dn,
+ Kopete::MetaContact *parent,
+ const int objectId, const int parentId, const int sequence )
+: Kopete::Contact( account, GroupWiseProtocol::dnToDotted( dn ), parent ), m_objectId( objectId ), m_parentId( parentId ),
+ m_sequence( sequence ), m_actionBlock( 0 ), m_archiving( false ), m_deleting( false ), m_messageReceivedOffline( false )
+{
+ if ( dn.find( '=' ) != -1 )
+ {
+ m_dn = dn;
+ }
+ connect( static_cast< GroupWiseAccount *>( account ), SIGNAL( privacyChanged( const QString &, bool ) ),
+ SLOT( receivePrivacyChanged( const QString &, bool ) ) );
+ setOnlineStatus( ( parent && parent->isTemporary() ) ? protocol()->groupwiseUnknown : protocol()->groupwiseOffline );
+}
+
+GroupWiseContact::~GroupWiseContact()
+{
+ // This is necessary because otherwise the userDetailsManager
+ // would not fetch details for this contact if they contact you
+ // again from off-contact-list.
+ if ( metaContact()->isTemporary() )
+ account()->client()->userDetailsManager()->removeContact( contactId() );
+}
+
+QString GroupWiseContact::dn() const
+{
+ return m_dn;
+}
+
+void GroupWiseContact::updateDetails( const ContactDetails & details )
+{
+ kdDebug( GROUPWISE_DEBUG_GLOBAL ) << k_funcinfo << endl;
+ if ( !details.cn.isNull() )
+ setProperty( protocol()->propCN, details.cn );
+ if ( !details.dn.isNull() )
+ m_dn = details.dn;
+ if ( !details.givenName.isNull() )
+ setProperty( protocol()->propGivenName, details.givenName );
+ if ( !details.surname.isNull() )
+ setProperty( protocol()->propLastName, details.surname );
+ if ( !details.fullName.isNull() )
+ setProperty( protocol()->propFullName, details.fullName );
+ m_archiving = details.archive;
+ if ( !details.awayMessage.isNull() )
+ setProperty( protocol()->propAwayMessage, details.awayMessage );
+
+ m_serverProperties = details.properties;
+
+ QMap<QString, QString>::Iterator it;
+ // work phone number
+ if ( ( it = m_serverProperties.find( "telephoneNumber" ) )
+ != m_serverProperties.end() )
+ setProperty( protocol()->propPhoneWork, it.data() );
+
+ // mobile phone number
+ if ( ( it = m_serverProperties.find( "mobile" ) )
+ != m_serverProperties.end() )
+ setProperty( protocol()->propPhoneMobile, it.data() );
+
+ // email
+ if ( ( it = m_serverProperties.find( "Internet EMail Address" ) )
+ != m_serverProperties.end() )
+ setProperty( protocol()->propEmail, it.data() );
+
+ if ( details.status != GroupWise::Invalid )
+ {
+ Kopete::OnlineStatus status = protocol()->gwStatusToKOS( details.status );
+ setOnlineStatus( status );
+ }
+}
+
+GroupWiseProtocol *GroupWiseContact::protocol()
+{
+ return static_cast<GroupWiseProtocol *>( Kopete::Contact::protocol() );
+}
+
+GroupWiseAccount *GroupWiseContact::account()
+{
+ return static_cast<GroupWiseAccount *>( Kopete::Contact::account() );
+}
+
+bool GroupWiseContact::isReachable()
+{
+ // When we are invisible we can't start a chat with others, but we don't make isReachable return false, because then we
+ // don't get any notification when we click on someone in the contact list. Instead we warn the user when they try to send a message,
+ // in GWChatSession
+ // (This is a GroupWise rule, not a problem in Kopete)
+
+ if ( account()->isConnected() && ( isOnline() || messageReceivedOffline() ) /* && account()->myself()->onlineStatus() != protocol()->groupwiseAppearOffline */)
+ return true;
+ if ( !account()->isConnected()/* || account()->myself()->onlineStatus() == protocol()->groupwiseAppearOffline*/ )
+ return false;
+
+ // fallback, something went wrong
+ return false;
+}
+
+void GroupWiseContact::serialize( QMap< QString, QString > &serializedData, QMap< QString, QString > & /* addressBookData */ )
+{
+ serializedData[ "DN" ] = m_dn;
+}
+
+Kopete::ChatSession * GroupWiseContact::manager( Kopete::Contact::CanCreateFlags canCreate )
+{
+ //kdDebug( GROUPWISE_DEBUG_GLOBAL ) << k_funcinfo << "called, canCreate: " << canCreate << endl;
+
+ Kopete::ContactPtrList chatMembers;
+ chatMembers.append( this );
+
+ return account()->chatSession( chatMembers, QString::null, canCreate );
+}
+
+QPtrList<KAction> *GroupWiseContact::customContextMenuActions()
+{
+ QPtrList<KAction> *m_actionCollection = new QPtrList<KAction>;
+
+ // Block/unblock Contact
+ QString label = account()->isContactBlocked( m_dn ) ? i18n( "Unblock User" ) : i18n( "Block User" );
+ if( !m_actionBlock )
+ {
+ m_actionBlock = new KAction( label, "msn_blocked",0, this, SLOT( slotBlock() ),
+ this, "actionBlock" );
+ }
+ else
+ m_actionBlock->setText( label );
+ m_actionBlock->setEnabled( account()->isConnected() );
+
+ m_actionCollection->append( m_actionBlock );
+
+ return m_actionCollection;
+}
+
+void GroupWiseContact::slotUserInfo()
+{
+ new GroupWiseContactProperties( this, Kopete::UI::Global::mainWidget(), "gwcontactproperties" );
+}
+
+QMap< QString, QString > GroupWiseContact::serverProperties()
+{
+ return m_serverProperties;
+}
+
+void GroupWiseContact::sendMessage( Kopete::Message &message )
+{
+ kdDebug( GROUPWISE_DEBUG_GLOBAL ) << k_funcinfo << endl;
+ manager()->appendMessage( message );
+ // tell the manager it was sent successfully
+ manager()->messageSucceeded();
+}
+
+void GroupWiseContact::deleteContact()
+{
+ account()->deleteContact( this );
+}
+
+void GroupWiseContact::sync( unsigned int)
+{
+ account()->syncContact( this );
+}
+
+void GroupWiseContact::slotBlock()
+{
+ kdDebug( GROUPWISE_DEBUG_GLOBAL ) << k_funcinfo << endl;
+ if ( account()->isConnected() )
+ {
+ if ( account()->isContactBlocked( m_dn ) )
+ account()->client()->privacyManager()->setAllow( m_dn );
+ else
+ account()->client()->privacyManager()->setDeny( m_dn );
+ }
+}
+
+void GroupWiseContact::receivePrivacyChanged( const QString & dn, bool allow )
+{
+ Q_UNUSED( allow );
+ if ( dn == m_dn ) // set the online status back to itself. this will set the blocking state
+ setOnlineStatus( this->onlineStatus() );
+}
+
+void GroupWiseContact::setOnlineStatus( const Kopete::OnlineStatus& status )
+{
+ setMessageReceivedOffline( false );
+ if ( status == protocol()->groupwiseAwayIdle && status != onlineStatus() )
+ setIdleTime( 1 );
+ else if ( onlineStatus() == protocol()->groupwiseAwayIdle && status != onlineStatus() )
+ setIdleTime( 0 );
+
+ if ( account()->isContactBlocked( m_dn ) && status.internalStatus() < 15 )
+ {
+ Kopete::Contact::setOnlineStatus(Kopete::OnlineStatus(status.status() , (status.weight()==0) ? 0 : (status.weight() -1) ,
+ protocol() , status.internalStatus()+15 , QString::fromLatin1("msn_blocked"),
+ i18n("%1|Blocked").arg( status.description() ) ) );
+ }
+ else
+ {
+ if(status.internalStatus() >= 15)
+ { //the user is not blocked, but the status is blocked
+ switch(status.internalStatus()-15)
+ {
+ case 0:
+ Kopete::Contact::setOnlineStatus( GroupWiseProtocol::protocol()->groupwiseUnknown );
+ break;
+ case 1:
+ Kopete::Contact::setOnlineStatus( GroupWiseProtocol::protocol()->groupwiseOffline );
+ break;
+ case 2:
+ Kopete::Contact::setOnlineStatus( GroupWiseProtocol::protocol()->groupwiseAvailable );
+ break;
+ case 3:
+ Kopete::Contact::setOnlineStatus( GroupWiseProtocol::protocol()->groupwiseBusy );
+ break;
+ case 4:
+ Kopete::Contact::setOnlineStatus( GroupWiseProtocol::protocol()->groupwiseAway );
+ break;
+ case 5:
+ Kopete::Contact::setOnlineStatus( GroupWiseProtocol::protocol()->groupwiseAwayIdle );
+ break;
+ default:
+ Kopete::Contact::setOnlineStatus( GroupWiseProtocol::protocol()->groupwiseUnknown );
+ break;
+ }
+ }
+ else
+ Kopete::Contact::setOnlineStatus(status);
+ }
+}
+
+bool GroupWiseContact::archiving() const
+{
+ return m_archiving;
+}
+
+bool GroupWiseContact::deleting() const
+{
+ return m_deleting;
+}
+
+void GroupWiseContact::setDeleting( bool deleting )
+{
+ m_deleting = deleting;
+}
+
+void GroupWiseContact::renamedOnServer()
+{
+ UpdateContactTask * uct = ( UpdateContactTask * )sender();
+ if ( uct->success() )
+ {
+ if( uct->displayName() !=
+ property( Kopete::Global::Properties::self()->nickName() ).value().toString() )
+ setProperty( Kopete::Global::Properties::self()->nickName(), uct->displayName() );
+ }
+ else
+ kdDebug( GROUPWISE_DEBUG_GLOBAL ) << k_funcinfo << "rename failed, return code: " << uct->statusCode() << endl;
+}
+
+void GroupWiseContact::setMessageReceivedOffline( bool on )
+{
+ m_messageReceivedOffline = on;
+}
+
+bool GroupWiseContact::messageReceivedOffline() const
+{
+ return m_messageReceivedOffline;
+}
+
+#include "gwcontact.moc"
+
+// vim: set noet ts=4 sts=4 sw=4:
+
diff --git a/kopete/protocols/groupwise/gwcontact.h b/kopete/protocols/groupwise/gwcontact.h
new file mode 100644
index 00000000..e5079387
--- /dev/null
+++ b/kopete/protocols/groupwise/gwcontact.h
@@ -0,0 +1,194 @@
+/*
+ gwcontact.h - Kopete GroupWise Protocol
+
+ Copyright (c) 2004 SUSE Linux AG http://www.suse.com
+
+ Based on Testbed
+ Copyright (c) 2003 by Will Stephenson <[email protected]>
+
+ Blocking status taken from MSN
+ Copyright (c) 2002 by Duncan Mac-Vicar Prett <[email protected]>
+ Copyright (c) 2002 by Ryan Cumming <[email protected]>
+ Copyright (c) 2002-2003 by Martijn Klingens <[email protected]>
+ Copyright (c) 2002-2004 by Olivier Goffart <ogoffart @ kde.org>
+
+ Kopete (c) 2002-2003 by the Kopete developers <[email protected]>
+
+ *************************************************************************
+ * *
+ * This library 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 GW_CONTACT_H
+#define GW_CONTACT_H
+
+#include <qdict.h>
+#include <qmap.h>
+
+#include "kopetecontact.h"
+#include "kopetemessage.h"
+
+#include "gwerror.h"
+#include "gwfield.h"
+#include "gwmessagemanager.h"
+
+class KAction;
+class KActionCollection;
+namespace Kopete { class Account; }
+class GroupWiseAccount;
+class GroupWiseChatSession;
+class GroupWiseProtocol;
+namespace Kopete { class MetaContact; }
+
+using namespace GroupWise;
+
+/**
+@author Will Stephenson
+*/
+class GroupWiseContact : public Kopete::Contact
+{
+ Q_OBJECT
+public:
+ /**
+ * Constructor
+ * @param account The GroupWiseAccount this belongs to.
+ * @param uniqueName The userId for this contact. May be a DN, in which case it will be converted to dotted format for the contactId and stored.
+ * @param parent The Kopete::MetaContact this contact is part of.
+ * @param objectId The contact's numeric object ID.
+ * @param parentId The ID of this contact's parent (folder).
+ * @param sequence This contact's sequence number (The position it appears in within its parent).
+ */
+ GroupWiseContact( Kopete::Account* account, const QString &uniqueName,
+ Kopete::MetaContact *parent,
+ const int objectId, const int parentId, const int sequence );
+
+ ~GroupWiseContact();
+
+ /**
+ * Access this contact's Kopete::Account subclass
+ */
+ GroupWiseAccount * account();
+
+ /**
+ * Access this contact's Kopete::Protocol subclass
+ */
+ GroupWiseProtocol * protocol();
+
+ /**
+ * Get the contact's DN (used for communications with the server, not the contactId )
+ */
+ QString dn() const;
+
+ /**
+ * Update the contact's status and metadata from the supplied fields
+ */
+ void updateDetails( const GroupWise::ContactDetails & details );
+
+ virtual bool isReachable();
+ /**
+ * Serialize the contact's data into a key-value map
+ * suitable for writing to a file
+ */
+ virtual void serialize(QMap< QString, QString >& serializedData,
+ QMap< QString, QString >& addressBookData);
+ /**
+ * Return the actions for this contact
+ */
+ virtual QPtrList<KAction> *customContextMenuActions();
+
+ /**
+ * Returns a Kopete::ChatSession associated with this contact
+ */
+ virtual Kopete::ChatSession *manager( Kopete::Contact::CanCreateFlags canCreate = Kopete::Contact::CannotCreate );
+
+ /**
+ * Access the contact's server properties
+ */
+ QMap< QString, QString > serverProperties();
+ /**
+ * Updates this contact's group membership and display name on the server
+ */
+ void sync( unsigned int);
+ /**
+ * Updates this contact's online status, including blocking status
+ */
+ void setOnlineStatus(const Kopete::OnlineStatus& status);
+ /**
+ * Are this contact's chats being administratively logged?
+ */
+ bool archiving() const;
+ /**
+ * Is this contact in the process of being deleted
+ */
+ bool deleting() const;
+ /**
+ * Mark this contact as being deleted
+ */
+ void setDeleting( bool deleting );
+ /**
+ * Marks this contact as having sent a message whilst apparently offline
+ */
+ void setMessageReceivedOffline( bool on );
+ /**
+ * Has this contact sent a message whilst apparently offline?
+ */
+ bool messageReceivedOffline() const;
+
+public slots:
+ /**
+ * Transmits an outgoing message to the server
+ * Called when the chat window send button has been pressed
+ * (in response to the relevant Kopete::ChatSession signal)
+ */
+ void sendMessage( Kopete::Message &message );
+ /**
+ * Delete this contact on the server
+ */
+ virtual void deleteContact();
+ /**
+ * Called when the call to rename the contact on the server has completed
+ */
+ void renamedOnServer();
+
+protected:
+ // debug function to see what message managers we have on the server
+ void dumpManagers();
+protected slots:
+ /**
+ * Show the contact's properties
+ */
+ void slotUserInfo();
+ /**
+ * Block or unblock the contact, toggle its current blocking state
+ */
+ void slotBlock();
+ /**
+ * Receive notification that this contact's privacy setting changed - update status
+ */
+ void receivePrivacyChanged( const QString &, bool );
+protected:
+ KActionCollection* m_actionCollection;
+
+ int m_objectId;
+ int m_parentId;
+ int m_sequence;
+ QString m_dn;
+ QString m_displayName;
+ KAction* m_actionPrefs;
+ KAction *m_actionBlock;
+ // Novell Messenger Properties, as received by the server.
+ // Unfortunately we don't the domain of the set of keys, so they are not easily mappable to KopeteContactProperties
+ QMap< QString, QString > m_serverProperties;
+ bool m_archiving;
+ // HACK: flag used to differentiate between 'all contact list instances gone while we are moving on the server'
+ // and 'all contact list instances gone because we wanted to delete them all'
+ bool m_deleting;
+ bool m_messageReceivedOffline;
+};
+
+#endif
diff --git a/kopete/protocols/groupwise/gwcontactlist.cpp b/kopete/protocols/groupwise/gwcontactlist.cpp
new file mode 100644
index 00000000..2af6d42a
--- /dev/null
+++ b/kopete/protocols/groupwise/gwcontactlist.cpp
@@ -0,0 +1,237 @@
+/*
+ gwcontactlist.cpp - Kopete GroupWise Protocol
+
+ Copyright (c) 2005 SUSE Linux Products GmbH http://www.suse.com
+
+ Kopete (c) 2002-2005 by the Kopete developers <[email protected]>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include <qobjectlist.h>
+
+#include <kdebug.h>
+
+#include "gwcontactlist.h"
+#include "gwerror.h" //debug area
+
+GWContactList::GWContactList( QObject * parent )
+ : QObject( parent ), rootFolder( new GWFolder( this, 0, 0, QString::null ) )
+{ }
+
+GWFolder * GWContactList::addFolder( unsigned int id, unsigned int sequence, const QString & displayName )
+{
+ if ( rootFolder )
+ return new GWFolder( rootFolder, id, sequence, displayName );
+ else
+ return 0;
+}
+
+GWContactInstance * GWContactList::addContactInstance( unsigned int id, unsigned int parent, unsigned int sequence, const QString & displayName, const QString & dn )
+{
+ QObjectList * l = queryList( "GWFolder", 0, false, true );
+ QObjectListIt it( *l ); // iterate over the buttons
+ QObject *obj;
+ GWContactInstance * contact = 0;
+ while ( (obj = it.current()) != 0 )
+ {
+ GWFolder * folder = ::qt_cast< GWFolder * >( obj );
+ if ( folder && folder->id == parent )
+ {
+ contact = new GWContactInstance( folder, id, sequence, displayName, dn );
+ break;
+ }
+ ++it;
+ }
+ delete l;
+ return contact;
+}
+
+GWFolder * GWContactList::findFolderById( unsigned int id )
+{
+ QObjectList * l = queryList( "GWFolder", 0, false, true );
+ QObjectListIt it( *l ); // iterate over the buttons
+ QObject *obj;
+ GWFolder * candidate, * folder = 0;
+ while ( (obj = it.current()) != 0 )
+ {
+ candidate = ::qt_cast< GWFolder * >( obj );
+ if ( candidate->id == id )
+ {
+ folder = candidate;
+ break;
+ }
+ ++it;
+ }
+ delete l;
+ return folder;
+}
+
+GWFolder * GWContactList::findFolderByName( const QString & displayName )
+{
+ QObjectList * l = queryList( "GWFolder", 0, false, true );
+ QObjectListIt it( *l ); // iterate over the buttons
+ QObject *obj;
+ GWFolder * folder = 0;
+ while ( (obj = it.current()) != 0 )
+ {
+ GWFolder * candidate = ::qt_cast< GWFolder * >( obj );
+ if ( candidate->displayName == displayName )
+ {
+ folder = candidate;
+ break;
+ }
+ ++it;
+ }
+ delete l;
+ return folder;
+}
+
+int GWContactList::maxSequenceNumber()
+{
+ QObjectList * l = queryList( "GWFolder", 0, false, true );
+ QObjectListIt it( *l ); // iterate over the buttons
+ QObject *obj;
+ unsigned int sequence = 0;
+ while ( (obj = it.current()) != 0 )
+ {
+ GWFolder * current = ::qt_cast< GWFolder * >( obj );
+ sequence = QMAX( sequence, current->sequence );
+ ++it;
+ }
+ delete l;
+ return sequence;
+}
+
+GWContactInstanceList GWContactList::instancesWithDn( const QString & dn )
+{
+ QObjectList * l = queryList( "GWContactInstance", 0, false, true );
+ QObjectListIt it( *l ); // iterate over the buttons
+ QObject *obj;
+ GWContactInstanceList matches;
+ while ( (obj = it.current()) != 0 )
+ {
+ ++it;
+ GWContactInstance * current = ::qt_cast<GWContactInstance *>( obj );
+ if ( current->dn == dn )
+ matches.append( current );
+ }
+ delete l;
+ return matches;
+}
+
+void GWContactList::removeInstance( GWContactListItem * instance )
+{
+ delete instance;
+}
+
+void GWContactList::removeInstanceById( unsigned int id )
+{
+ QObjectList * l = queryList( "GWContactInstance", 0, false, true );
+ QObjectListIt it( *l ); // iterate over the buttons
+ QObject *obj;
+ GWContactInstanceList matches;
+ while ( (obj = it.current()) != 0 )
+ {
+ ++it;
+ GWContactInstance * current = ::qt_cast<GWContactInstance *>( obj );
+ if ( current->id == id )
+ {
+ delete current;
+ break;
+ }
+ }
+ delete l;
+}
+
+void GWContactList::dump()
+{
+ kdDebug(GROUPWISE_DEBUG_GLOBAL) << k_funcinfo << endl;
+ const QObjectList * l = children();
+ if ( l && !l->isEmpty() )
+ {
+ QObjectListIt it( *l ); // iterate over the buttons
+ QObject *obj;
+ while ( (obj = it.current()) != 0 )
+ {
+ GWFolder * folder = ::qt_cast< GWFolder * >( obj );
+ if ( folder )
+ folder->dump( 1 );
+ ++it;
+ }
+ }
+ else
+ kdDebug ( GROUPWISE_DEBUG_GLOBAL ) << " contact list is empty." << endl;
+}
+
+void GWContactList::clear()
+{
+ kdDebug(GROUPWISE_DEBUG_GLOBAL) << k_funcinfo << endl;
+ const QObjectList * l = children();
+ if ( l && !l->isEmpty() )
+ {
+ QObjectListIt it( *l );
+ QObject *obj;
+ while ( (obj = it.current()) != 0 )
+ {
+ delete obj;
+ ++it;
+ }
+ }
+}
+
+GWContactListItem::GWContactListItem( QObject * parent, unsigned int theId, unsigned int theSequence, const QString & theDisplayName ) :
+ QObject( parent), id( theId ), sequence( theSequence ), displayName( theDisplayName )
+{ }
+
+GWFolder::GWFolder( QObject * parent, unsigned int theId, unsigned int theSequence, const QString & theDisplayName ) :
+ GWContactListItem( parent, theId, theSequence, theDisplayName )
+{ }
+
+void GWFolder::dump( unsigned int depth )
+{
+ QString s;
+ s.fill( ' ', ++depth * 2 );
+ kdDebug( GROUPWISE_DEBUG_GLOBAL ) << s <<"Folder " << displayName << " id: " << id << " contains: " << endl;
+ const QObjectList * l = children();
+ if ( l )
+ {
+ QObjectListIt it( *l ); // iterate over the buttons
+ QObject *obj;
+ while ( (obj = it.current()) != 0 )
+ {
+ ++it;
+ GWContactInstance * instance = ::qt_cast< GWContactInstance * >( obj );
+ if (instance)
+ instance->dump( depth );
+ else
+ {
+ GWFolder * folder = ::qt_cast< GWFolder * >( obj );
+ if ( folder )
+ folder->dump( depth );
+ }
+ }
+ }
+ else
+ kdDebug( GROUPWISE_DEBUG_GLOBAL ) << s << " no contacts." << endl;
+}
+
+GWContactInstance::GWContactInstance( QObject * parent, unsigned int theId, unsigned int theSequence, const QString & theDisplayName, const QString & theDn ) :
+ GWContactListItem( parent, theId, theSequence, theDisplayName ), dn( theDn )
+{ }
+
+void GWContactInstance::dump( unsigned int depth )
+{
+ QString s;
+ s.fill( ' ', ++depth * 2 );
+ kdDebug( GROUPWISE_DEBUG_GLOBAL ) << s << "Contact " << displayName << " id: " << id << " dn: " << dn << endl;
+}
+#include "gwcontactlist.moc"
+
diff --git a/kopete/protocols/groupwise/gwcontactlist.h b/kopete/protocols/groupwise/gwcontactlist.h
new file mode 100644
index 00000000..e596b96c
--- /dev/null
+++ b/kopete/protocols/groupwise/gwcontactlist.h
@@ -0,0 +1,90 @@
+/*
+ gwcontactlist.h - Kopete GroupWise Protocol
+
+ Copyright (c) 2005 SUSE Linux Products GmbH http://www.suse.com
+
+ Kopete (c) 2002-2005 by the Kopete developers <[email protected]>
+
+ *************************************************************************
+ * *
+ * This library 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. *
+ * *
+ *************************************************************************
+*/
+
+// GROUPWISE SERVER SIDE CONTACT LIST MODEL
+
+#include <qobject.h>
+
+#ifndef GW_CONTACTLIST_H
+#define GW_CONTACTLIST_H
+
+class GWFolder;
+class GWContactInstance;
+class GWContactListItem;
+
+typedef QValueList<GWContactInstance *> GWContactInstanceList;
+
+ /**
+ * These functions model the server side contact list structure enough to allow Kopete to manipulate it correctly
+ * In GroupWise, a contactlist is composed of folders, containing contacts. But the contacts don't record which
+ * folders they are in. Instead, each contact entry represents an instance of that contact within the list.
+ * In Kopete's model, this looks like duplicate contacts (illegal), so instead we have unique contacts,
+ * each (by way of its metacontact) knowing membership of potentially >1 KopeteGroups. Contacts contain a list of the
+ * server side list instances. Contact list management operations affect this list, which is updated during every
+ * operation. Having this list allows us to update the server side contact list and keep changes synchronised across
+ * different clients.
+ * The list is volatile - it is not stored in stable storage, but is purged on disconnect and recreated at login.
+ */
+class GWContactList : public QObject
+{
+Q_OBJECT
+public:
+ GWContactList( QObject * parent );
+ GWFolder * addFolder( unsigned int id, unsigned int sequence, const QString & displayName );
+ GWContactInstance * addContactInstance( unsigned int id, unsigned int parent, unsigned int sequence, const QString & displayName, const QString & dn );
+ GWFolder * findFolderById( unsigned int id );
+ GWFolder * findFolderByName( const QString & name );
+ GWContactInstanceList instancesWithDn( const QString & dn );
+ void removeInstance( GWContactListItem * instance );
+ void removeInstanceById( unsigned int id );
+ int maxSequenceNumber();
+ virtual void dump();
+ void clear();
+ GWFolder * rootFolder;
+};
+
+class GWContactListItem : public QObject
+{
+Q_OBJECT
+public:
+ GWContactListItem( QObject * parent, unsigned int theId, unsigned int theSequence, const QString & theDisplayName );
+
+ unsigned int id; // OBJECT_ID
+ unsigned int sequence; // SEQUENCE_NUMBER
+ QString displayName; // DISPLAY_NAME
+};
+
+class GWFolder : public GWContactListItem
+{
+Q_OBJECT
+public:
+ GWFolder( QObject * parent, unsigned int theId, unsigned int theSequence, const QString & theDisplayName );
+ virtual void dump( unsigned int depth );
+};
+
+class GWContactInstance : public GWContactListItem
+{
+Q_OBJECT
+public:
+ GWContactInstance( QObject * parent, unsigned int theId, unsigned int theSequence, const QString & theDisplayName, const QString & theDn );
+ QString dn; // DN
+ virtual void dump( unsigned int depth );
+};
+
+// END OF SERVER SIDE CONTACT LIST MODEL
+
+#endif
diff --git a/kopete/protocols/groupwise/gwmessagemanager.cpp b/kopete/protocols/groupwise/gwmessagemanager.cpp
new file mode 100644
index 00000000..d9467dfd
--- /dev/null
+++ b/kopete/protocols/groupwise/gwmessagemanager.cpp
@@ -0,0 +1,516 @@
+//
+// C++ Implementation: gwmessagemanager
+//
+// Description:
+//
+//
+// Author: SUSE AG <>, (C) 2004
+//
+// Copyright: See COPYING file that comes with this distribution
+//
+//
+
+#include <qlabel.h>
+#include <qvalidator.h>
+#include <kdebug.h>
+#include <kdialogbase.h>
+#include <kiconloader.h>
+#include <kinputdialog.h>
+#include <klocale.h>
+#include <kmainwindow.h>
+#include <kmessagebox.h>
+#include <kpopupmenu.h>
+#include <kshortcut.h>
+
+#include <kopetecontact.h>
+#include <kopetecontactaction.h>
+#include <kopetemetacontact.h>
+#include <kopetechatsessionmanager.h>
+#include <kopeteprotocol.h>
+#include <kopeteuiglobal.h>
+#include <kopeteview.h>
+
+#include "client.h"
+#include "gwaccount.h"
+#include "gwcontact.h"
+#include "gwerror.h"
+#include "gwprotocol.h"
+#include "gwsearch.h"
+
+#include "gwmessagemanager.h"
+
+GroupWiseChatSession::GroupWiseChatSession(const Kopete::Contact* user, Kopete::ContactPtrList others, Kopete::Protocol* protocol, const GroupWise::ConferenceGuid & guid, int id, const char* name): Kopete::ChatSession(user, others, protocol, name), m_guid( guid ), m_flags( 0 ), m_searchDlg( 0 ), m_memberCount( others.count() )
+{
+ Q_UNUSED( id );
+ static uint s_id=0;
+ m_mmId=++s_id;
+
+ kdDebug ( GROUPWISE_DEBUG_GLOBAL ) << k_funcinfo << "New message manager for " << user->contactId() << endl;
+
+ // Needed because this is (indirectly) a KXMLGuiClient, so it can find the gui description .rc file
+ setInstance( protocol->instance() );
+
+ // make sure Kopete knows about this instance
+ Kopete::ChatSessionManager::self()->registerChatSession ( this );
+
+ connect ( this, SIGNAL( messageSent ( Kopete::Message &, Kopete::ChatSession * ) ),
+ SLOT( slotMessageSent ( Kopete::Message &, Kopete::ChatSession * ) ) );
+ connect( this, SIGNAL( myselfTyping ( bool ) ), SLOT( slotSendTypingNotification ( bool ) ) );
+ connect( account(), SIGNAL( contactTyping( const ConferenceEvent & ) ),
+ SLOT( slotGotTypingNotification( const ConferenceEvent & ) ) );
+ connect( account(), SIGNAL( contactNotTyping( const ConferenceEvent & ) ),
+ SLOT( slotGotNotTypingNotification( const ConferenceEvent & ) ) );
+
+ // Set up the Invite menu
+ m_actionInvite = new KActionMenu( i18n( "&Invite" ), actionCollection() , "gwInvite" );
+ connect( m_actionInvite->popupMenu(), SIGNAL( aboutToShow() ), this, SLOT(slotActionInviteAboutToShow() ) ) ;
+
+ m_secure = new KAction( i18n( "Security Status" ), "encrypted", KShortcut(), this, SLOT( slotShowSecurity() ), actionCollection(), "gwSecureChat" );
+ m_secure->setToolTip( i18n( "Conversation is secure" ) );
+
+ m_logging = new KAction( i18n( "Archiving Status" ), "logchat", KShortcut(), this, SLOT( slotShowArchiving() ), actionCollection(), "gwLoggingChat" );
+ updateArchiving();
+
+ setXMLFile("gwchatui.rc");
+ setMayInvite( true );
+
+ m_invitees.setAutoDelete( true );
+}
+
+GroupWiseChatSession::~GroupWiseChatSession()
+{
+ emit leavingConference( this );
+}
+
+uint GroupWiseChatSession::mmId() const
+{
+ return m_mmId;
+}
+
+void GroupWiseChatSession::setGuid( const GroupWise::ConferenceGuid & guid )
+{
+ if ( m_guid.isEmpty() )
+ {
+ kdDebug( GROUPWISE_DEBUG_GLOBAL ) << k_funcinfo << "setting GUID to: " << guid << endl;
+ m_guid = guid;
+ }
+ else
+ kdDebug( GROUPWISE_DEBUG_GLOBAL ) << k_funcinfo << "attempted to change the conference's GUID when already set!" << endl;
+}
+
+bool GroupWiseChatSession::closed()
+{
+ return m_flags & GroupWise::Closed;
+}
+
+bool GroupWiseChatSession::logging()
+{
+ return m_flags & GroupWise::Logging;
+}
+
+bool GroupWiseChatSession::secure()
+{
+ return m_flags & GroupWise::Secure;
+}
+
+void GroupWiseChatSession::setClosed()
+{
+ kdDebug( GROUPWISE_DEBUG_GLOBAL ) << k_funcinfo << " Conference " << m_guid << " is now Closed " << endl;
+ m_guid = QString::null;
+ m_flags = m_flags | GroupWise::Closed;
+}
+
+void GroupWiseChatSession::setLogging( bool logging )
+{
+ if ( logging )
+ m_flags = m_flags | GroupWise::Logging;
+ else
+ m_flags = m_flags & !GroupWise::Logging;
+}
+
+void GroupWiseChatSession::setSecure( bool secure )
+{
+ if ( secure )
+ m_flags = m_flags | GroupWise::Secure;
+ else
+ m_flags = m_flags & !GroupWise::Secure;
+}
+
+GroupWiseAccount * GroupWiseChatSession::account()
+{
+ return static_cast<GroupWiseAccount *>( Kopete::ChatSession::account() );
+}
+
+void GroupWiseChatSession::createConference()
+{
+ if ( m_guid.isEmpty() )
+ {
+ kdDebug ( GROUPWISE_DEBUG_GLOBAL ) << k_funcinfo << endl;
+ // form a list of invitees
+ QStringList invitees;
+ Kopete::ContactPtrList chatMembers = members();
+ for ( Kopete::Contact * contact = chatMembers.first(); contact; contact = chatMembers.next() )
+ {
+ invitees.append( static_cast< GroupWiseContact * >( contact )->dn() );
+ }
+ // this is where we will set the GUID and send any pending messages
+ connect( account(), SIGNAL( conferenceCreated( const int, const GroupWise::ConferenceGuid & ) ), SLOT( receiveGuid( const int, const GroupWise::ConferenceGuid & ) ) );
+ connect( account(), SIGNAL( conferenceCreationFailed( const int, const int ) ), SLOT( slotCreationFailed( const int, const int ) ) );
+
+ // create the conference
+ account()->createConference( mmId(), invitees );
+ }
+ else
+ kdDebug ( GROUPWISE_DEBUG_GLOBAL ) << k_funcinfo << " tried to create conference on the server when it was already instantiated" << endl;
+}
+
+void GroupWiseChatSession::receiveGuid( const int newMmId, const GroupWise::ConferenceGuid & guid )
+{
+ if ( newMmId == mmId() )
+ {
+ kdDebug ( GROUPWISE_DEBUG_GLOBAL ) << k_funcinfo << " got GUID from server" << endl;
+ m_memberCount = members().count();
+ setGuid( guid );
+ // re-add all the members. This is because when the last member leaves the conference,
+ // they are removed from the chat member list GUI. By re-adding them here, we guarantee they appear
+ // in the UI again, at the price of a debug message when starting up a new chatwindow
+
+ QPtrListIterator< Kopete::Contact > it( members() );
+ Kopete::Contact * contact;
+ while ( ( contact = it.current() ) )
+ {
+ ++it;
+ addContact( contact, true );
+ }
+
+ // notify the contact(s) using this message manager that it's been instantiated on the server
+ emit conferenceCreated();
+ // TODO: send invitations if we're not inviting in the conf create...
+ dequeueMessagesAndInvites();
+ }
+}
+
+void GroupWiseChatSession::slotCreationFailed( const int failedId, const int statusCode )
+{
+ if ( failedId == mmId() )
+ {
+ kdDebug ( GROUPWISE_DEBUG_GLOBAL ) << k_funcinfo << " couldn't start a chat, no GUID.\n" << endl;
+ //emit creationFailed();
+ Kopete::Message failureNotify = Kopete::Message( myself(), members(), i18n("An error occurred when trying to start a chat: %1").arg( statusCode ), Kopete::Message::Internal, Kopete::Message::PlainText);
+ appendMessage( failureNotify );
+ setClosed();
+ }
+}
+
+void GroupWiseChatSession::slotSendTypingNotification( bool typing )
+{
+ // only send a notification if we've got a conference going and we are not Appear Offline
+ if ( !m_guid.isEmpty() && m_memberCount &&
+ ( account()->myself()->onlineStatus() != GroupWiseProtocol::protocol()->groupwiseAppearOffline ) )
+ account()->client()->sendTyping( guid(), typing );
+}
+
+void GroupWiseChatSession::slotMessageSent( Kopete::Message & message, Kopete::ChatSession * )
+{
+ kdDebug ( GROUPWISE_DEBUG_GLOBAL ) << k_funcinfo << endl;
+ if( account()->isConnected() )
+ {
+ /*if ( closed() )
+ {
+ Kopete::Message failureNotify = Kopete::Message( myself(), members(), i18n("Your message could not be sent. This conversation has been closed by the server, because all the other participants left or declined invitations. "), Kopete::Message::Internal, Kopete::Message::PlainText);
+ appendMessage( failureNotify );
+ messageSucceeded();
+ }
+ else*/ if ( account()->myself()->onlineStatus() == ( static_cast<GroupWiseProtocol *>( protocol() ) )->groupwiseAppearOffline )
+ {
+ Kopete::Message failureNotify = Kopete::Message( myself(), members(), i18n("Your message could not be sent. You cannot send messages while your status is Appear Offline. "), Kopete::Message::Internal, Kopete::Message::PlainText);
+ appendMessage( failureNotify );
+ messageSucceeded();
+ }
+ else
+ {
+ // if the conference has not been instantiated yet, or if all the members have left
+ if ( m_guid.isEmpty() || m_memberCount == 0 )
+ {
+ // if there are still invitees, the conference is instantiated, and there are only
+ if ( m_invitees.count() )
+ {
+ // the message won't go anywhere, as there's noone there except invitees, but we warn the user
+ // when the last participant leaves.
+ messageSucceeded();
+ }
+ else
+ {
+ kdDebug ( GROUPWISE_DEBUG_GLOBAL ) << "waiting for server to create a conference, queuing message" << endl;
+ // the conference hasn't been instantiated on the server yet, so queue the message
+ m_guid = ConferenceGuid();
+ createConference();
+ m_pendingOutgoingMessages.append( message );
+ }
+ }
+ else
+ {
+ kdDebug ( GROUPWISE_DEBUG_GLOBAL ) << "sending message" << endl;
+ account()->sendMessage( guid(), message );
+ // we could wait until the server acks our send,
+ // but we'd need a UID for outgoing messages and a list to track them
+ appendMessage( message );
+ messageSucceeded();
+ }
+ }
+ }
+}
+
+void GroupWiseChatSession::slotGotTypingNotification( const ConferenceEvent& event )
+{
+ if ( event.guid == guid() )
+ receivedTypingMsg( static_cast<GroupWiseProtocol *>( protocol() )->dnToDotted( event.user ), true );
+}
+
+void GroupWiseChatSession::slotGotNotTypingNotification( const ConferenceEvent& event )
+{
+ if ( event.guid == guid() )
+ receivedTypingMsg( static_cast<GroupWiseProtocol *>( protocol() )->dnToDotted( event.user ), false );
+}
+
+void GroupWiseChatSession::dequeueMessagesAndInvites()
+{
+ kdDebug ( GROUPWISE_DEBUG_GLOBAL ) << k_funcinfo << endl;
+ for ( QValueListIterator< Kopete::Message > it = m_pendingOutgoingMessages.begin();
+ it != m_pendingOutgoingMessages.end();
+ ++it )
+ {
+ slotMessageSent( *it, this );
+ }
+ m_pendingOutgoingMessages.clear();
+ QPtrListIterator< Kopete::Contact > it( m_pendingInvites );
+ Kopete::Contact * contact;
+ while ( ( contact = it.current() ) )
+ {
+ ++it;
+ slotInviteContact( contact );
+ }
+ m_pendingInvites.clear();
+}
+
+void GroupWiseChatSession::slotActionInviteAboutToShow()
+{
+ // We can't simply insert KAction in this menu bebause we don't know when to delete them.
+ // items inserted with insert items are automatically deleted when we call clear
+
+ m_inviteActions.setAutoDelete(true);
+ m_inviteActions.clear();
+
+ m_actionInvite->popupMenu()->clear();
+
+
+ QDictIterator<Kopete::Contact> it( account()->contacts() );
+ for( ; it.current(); ++it )
+ {
+ if( !members().contains( it.current() ) && it.current()->isOnline() && it.current() != myself() )
+ {
+ KAction *a=new KopeteContactAction( it.current(), this,
+ SLOT( slotInviteContact( Kopete::Contact * ) ), m_actionInvite );
+ m_actionInvite->insert( a );
+ m_inviteActions.append( a ) ;
+ }
+ }
+ // Invite someone off-list
+ KAction *b=new KAction( i18n ("&Other..."), 0, this, SLOT( slotInviteOtherContact() ), m_actionInvite, "actionOther" );
+ m_actionInvite->insert( b );
+ m_inviteActions.append( b ) ;
+}
+
+void GroupWiseChatSession::slotInviteContact( Kopete::Contact * contact )
+{
+ if ( m_guid.isEmpty() )
+ {
+ m_pendingInvites.append( contact );
+ createConference();
+ }
+ else
+ {
+ QWidget * w = view(false) ? dynamic_cast<KMainWindow*>( view(false)->mainWidget()->topLevelWidget() ) : 0L;
+
+ bool ok;
+ QRegExp rx( ".*" );
+ QRegExpValidator validator( rx, this );
+ QString inviteMessage = KInputDialog::getText( i18n( "Enter Invitation Message" ),
+ i18n( "Enter the reason for the invitation, or leave blank for no reason:" ), QString(),
+ &ok, w ? w : Kopete::UI::Global::mainWidget(), "invitemessagedlg", &validator );
+ if ( ok )
+ {
+ GroupWiseContact * gwc = static_cast< GroupWiseContact *>( contact );
+ static_cast< GroupWiseAccount * >(account())->sendInvitation( m_guid, gwc->dn(), inviteMessage );
+ }
+ }
+}
+
+void GroupWiseChatSession::inviteContact( const QString &contactId )
+{
+ Kopete::Contact * contact = account()->contacts()[ contactId ];
+ if ( contact )
+ slotInviteContact( contact );
+}
+
+void GroupWiseChatSession::slotInviteOtherContact()
+{
+ if ( !m_searchDlg )
+ {
+ // show search dialog
+ QWidget * w = ( view(false) ? dynamic_cast<KMainWindow*>( view(false)->mainWidget()->topLevelWidget() ) :
+ Kopete::UI::Global::mainWidget() );
+ m_searchDlg = new KDialogBase( w, "invitesearchdialog", false, i18n( "Search for Contact to Invite" ), KDialogBase::Ok|KDialogBase::Cancel );
+ m_search = new GroupWiseContactSearch( account(), QListView::Single, true, m_searchDlg, "invitesearchwidget" );
+ m_searchDlg->setMainWidget( m_search );
+ connect( m_search, SIGNAL( selectionValidates( bool ) ), m_searchDlg, SLOT( enableButtonOK( bool ) ) );
+ m_searchDlg->enableButtonOK( false );
+ }
+ m_searchDlg->show();
+}
+
+void GroupWiseChatSession::slotSearchedForUsers()
+{
+ // create an item for each result, in the block list
+ QValueList< ContactDetails > selected = m_search->selectedResults();
+ if ( selected.count() )
+ {
+ QWidget * w = ( view(false) ? dynamic_cast<KMainWindow*>( view(false)->mainWidget()->topLevelWidget() ) :
+ Kopete::UI::Global::mainWidget() );
+ ContactDetails cd = selected.first();
+ bool ok;
+ QRegExp rx( ".*" );
+ QRegExpValidator validator( rx, this );
+ QString inviteMessage = KInputDialog::getText( i18n( "Enter Invitation Message" ),
+ i18n( "Enter the reason for the invitation, or leave blank for no reason:" ), QString(),
+ &ok, w , "invitemessagedlg", &validator );
+ if ( ok )
+ {
+ account()->sendInvitation( m_guid, cd.dn, inviteMessage );
+ }
+ }
+}
+
+void GroupWiseChatSession::addInvitee( const Kopete::Contact * c )
+{
+ // create a placeholder contact for each invitee
+ kdDebug ( GROUPWISE_DEBUG_GLOBAL ) << k_funcinfo << endl;
+ QString pending = i18n("label attached to contacts who have been invited but are yet to join a chat", "(pending)");
+ Kopete::MetaContact * inviteeMC = new Kopete::MetaContact();
+ inviteeMC->setDisplayName( c->metaContact()->displayName() + pending );
+ GroupWiseContact * invitee = new GroupWiseContact( account(), c->contactId() + " " + pending, inviteeMC, 0, 0, 0 );
+ invitee->setOnlineStatus( c->onlineStatus() );
+ // TODO: we could set all the placeholder's properties etc here too
+ addContact( invitee, true );
+ m_invitees.append( invitee );
+}
+
+void GroupWiseChatSession::joined( GroupWiseContact * c )
+{
+ // we add the real contact before removing the placeholder,
+ // because otherwise KMM will delete itself when the last member leaves.
+ addContact( c );
+
+ // look for the invitee and remove it
+ Kopete::Contact * pending;
+ for ( pending = m_invitees.first(); pending; pending = m_invitees.next() )
+ {
+ if ( pending->contactId().startsWith( c->contactId() ) )
+ {
+ removeContact( pending, QString::null, Kopete::Message::PlainText, true );
+ break;
+ }
+ }
+
+ m_invitees.remove( pending );
+
+ updateArchiving();
+
+ ++m_memberCount;
+}
+
+void GroupWiseChatSession::left( GroupWiseContact * c )
+{
+ kdDebug( GROUPWISE_DEBUG_GLOBAL ) << k_funcinfo << endl;
+ removeContact( c );
+ --m_memberCount;
+
+ updateArchiving();
+
+ if ( m_memberCount == 0 )
+ {
+ if ( m_invitees.count() )
+ {
+ Kopete::Message failureNotify = Kopete::Message( myself(), members(),
+ i18n("All the other participants have left, and other invitations are still pending. Your messages will not be delivered until someone else joins the chat."),
+ Kopete::Message::Internal, Kopete::Message::PlainText );
+ appendMessage( failureNotify );
+ }
+ else
+ setClosed();
+ }
+}
+
+void GroupWiseChatSession::inviteDeclined( GroupWiseContact * c )
+{
+ // look for the invitee and remove it
+ Kopete::Contact * pending;
+ for ( pending = m_invitees.first(); pending; pending = m_invitees.next() )
+ {
+ if ( pending->contactId().startsWith( c->contactId() ) )
+ {
+ removeContact( pending, QString::null, Kopete::Message::PlainText, true );
+ break;
+ }
+ }
+ m_invitees.remove( pending );
+
+ QString from = c->metaContact()->displayName();
+
+ Kopete::Message declined = Kopete::Message( myself(), members(),
+ i18n("%1 has rejected an invitation to join this conversation.").arg( from ),
+ Kopete::Message::Internal, Kopete::Message::PlainText );
+ appendMessage( declined );
+}
+
+void GroupWiseChatSession::updateArchiving()
+{
+ bool archiving = false;
+ QPtrListIterator< Kopete::Contact > it( members() );
+ GroupWiseContact * contact;
+ while ( ( contact = static_cast<GroupWiseContact*>( it.current() ) ) )
+ {
+ ++it;
+ if ( contact->archiving() )
+ {
+ archiving = true;
+ break;
+ }
+ }
+ if ( archiving )
+ {
+ m_logging->setEnabled( true );
+ m_logging->setToolTip( i18n( "Conversation is being administratively logged" ) );
+ }
+ else
+ {
+ m_logging->setEnabled( false );
+ m_logging->setToolTip( i18n( "Conversation is not being administratively logged" ) );
+ }
+}
+
+void GroupWiseChatSession::slotShowSecurity()
+{
+ QWidget * w = ( view(false) ? dynamic_cast<KMainWindow*>( view(false)->mainWidget()->topLevelWidget() ) :
+ Kopete::UI::Global::mainWidget() );
+ KMessageBox::queuedMessageBox( w, KMessageBox::Information, i18n( "This conversation is secured with SSL security." ), i18n("Security Status" ) );
+}
+
+void GroupWiseChatSession::slotShowArchiving()
+{
+ QWidget * w = ( view(false) ? dynamic_cast<KMainWindow*>( view(false)->mainWidget()->topLevelWidget() ) :
+ Kopete::UI::Global::mainWidget() );
+ KMessageBox::queuedMessageBox( w, KMessageBox::Information, i18n( "This conversation is being logged administratively." ), i18n("Archiving Status" ) );
+}
+
+#include "gwmessagemanager.moc"
diff --git a/kopete/protocols/groupwise/gwmessagemanager.h b/kopete/protocols/groupwise/gwmessagemanager.h
new file mode 100644
index 00000000..5413fd23
--- /dev/null
+++ b/kopete/protocols/groupwise/gwmessagemanager.h
@@ -0,0 +1,177 @@
+//
+// C++ Interface: gwmessagemanager
+//
+// Description:
+//
+//
+// Author: SUSE AG <>, (C) 2004
+//
+// Copyright: See COPYING file that comes with this distribution
+//
+//
+#ifndef GWMESSAGEMANAGER_H
+#define GWMESSAGEMANAGER_H
+
+#include <qptrqueue.h>
+#include <kopetemessage.h>
+#include <kopetechatsession.h>
+
+#include "gwerror.h"
+
+class QLabel;
+class KAction;
+class KActionMenu;
+class KDialogBase;
+class GroupWiseAccount;
+class GroupWiseContact;
+class GroupWiseContactSearch;
+/**
+ * Specialised message manager, which tracks the GUID used by GroupWise to uniquely identify a given chat, and provides invite actions and logging and security indicators. To instantiate call @ref GroupWiseAccount::chatSession().
+ * @author SUSE AG
+*/
+
+using namespace GroupWise;
+
+class GroupWiseChatSession : public Kopete::ChatSession
+{
+Q_OBJECT
+
+friend class GroupWiseAccount;
+
+public:
+ /**
+ * The destructor emits leavingConference so that the account can tell the server that the user has left the chat
+ */
+ ~GroupWiseChatSession();
+ /**
+ * The conference's globally unique identifier, which is given to it by the server
+ */
+ ConferenceGuid guid() const { return m_guid; }
+ /**
+ * Change the GUID
+ */
+ void setGuid( const ConferenceGuid & guid );
+ /**
+ * Utility account access
+ */
+ GroupWiseAccount * account();
+ /**
+ * Accessors and mutators for secure chat, logged chat, and closed conference flags
+ */
+ void setSecure( bool secure );
+ void setLogging( bool logged );
+ void setClosed();
+ bool secure();
+ bool logging();
+ bool closed();
+ /**
+ * Add invitees to the conference
+ */
+ void addInvitee( const Kopete::Contact * );
+ /**
+ * Add members to the conference
+ */
+ void joined( GroupWiseContact * );
+ /**
+ * Remove members from conference
+ */
+ void left( GroupWiseContact * );
+ /**
+ * An invitation was declined
+ */
+ void inviteDeclined( GroupWiseContact * );
+ /**
+ * Check whether the conversation being administratively logged and update the UI to indicate this
+ */
+ void updateArchiving();
+ /**
+ * Reimplemented from Kopete::ChatSession - invites contacts via DND
+ */
+ virtual void inviteContact(const QString& );
+signals:
+ /**
+ * Tell the contact we got a GUID so it can route incoming messages here.
+ */
+ void conferenceCreated();
+ /**
+ * Tell the account that the GroupWiseChatSession is closing so it can tell the server that the user has left the conference
+ */
+ void leavingConference( GroupWiseChatSession * );
+protected:
+ /**
+ * Start the process of creating a conference for this GWMM on the server.
+ */
+ void createConference();
+ /**
+ * Sends any messages and invitations that were queued while waiting for the conference to be created
+ */
+ void dequeueMessagesAndInvites();
+protected slots:
+ /**
+ * Receive the GUID returned by the server when we start a chat.
+ * @param mmId Message Manager ID, used to determine if this GUID is meant for this message manager
+ * @param guid The GUID allotted us by the server.
+ */
+ void receiveGuid( const int mmId, const GroupWise::ConferenceGuid & guid );
+ /**
+ * An attempt to create a conference on the server failed.
+ * @param mmId Message Manager ID to see if the failure refers to this message manager
+ */
+ void slotCreationFailed( const int mmId,const int statusCode );
+
+ void slotSendTypingNotification ( bool typing );
+ void slotMessageSent( Kopete::Message &message, Kopete::ChatSession * );
+ // TODO: slots for us leaving conference, us inviting someone, someone joining, someone leaving, someone sending an invitation, getting typing?
+ void slotGotTypingNotification( const ConferenceEvent & );
+ void slotGotNotTypingNotification( const ConferenceEvent & );
+ /**
+ * Popupulate the menu of invitable contacts
+ */
+ void slotActionInviteAboutToShow();
+ /**
+ * Invite a contact to join this chat
+ */
+ void slotInviteContact( Kopete::Contact * );
+ /**
+ * Show the search dialog to invite another contact to the chat
+ */
+ void slotInviteOtherContact();
+ /**
+ * Process the response from the search dialog; send the actual invitation
+ */
+ void slotSearchedForUsers();
+
+ void slotShowSecurity();
+ void slotShowArchiving();
+private:
+
+ GroupWiseChatSession(const Kopete::Contact* user, Kopete::ContactPtrList others, Kopete::Protocol* protocol, const ConferenceGuid & guid, int id = 0, const char* name = 0);
+
+ ConferenceGuid m_guid; // The conference's globally unique identifier, which is given to it by the server
+ int m_flags; // flags for secure connections, central logging and "conference closed" as given by the server
+
+ QValueList< Kopete::Message > m_pendingOutgoingMessages; // messages queued while we wait for the server to tell us the conference is created.
+ Kopete::ContactPtrList m_pendingInvites; // people we wanted to invite to the conference, queued while waiting for the conference to be created.
+ KActionMenu *m_actionInvite;
+ QPtrList<KAction> m_inviteActions;
+ // labels showing secure and logging status
+ KAction *m_secure;
+ KAction *m_logging;
+ // search widget and dialog used for inviting contacts
+ GroupWiseContactSearch * m_search;
+ KDialogBase * m_searchDlg;
+ // contacts who have been invited to join but have not yet joined the chat
+ Kopete::ContactPtrList m_invitees;
+ // track the number of members actually in the chat
+ uint m_memberCount;
+
+ /**
+ * return an unique identifier for that kmm
+ * @todo check it!
+ */
+ uint mmId() const;
+ uint m_mmId;
+
+};
+
+#endif
diff --git a/kopete/protocols/groupwise/gwprotocol.cpp b/kopete/protocols/groupwise/gwprotocol.cpp
new file mode 100644
index 00000000..dcf92a17
--- /dev/null
+++ b/kopete/protocols/groupwise/gwprotocol.cpp
@@ -0,0 +1,283 @@
+/*
+ gwprotocol.cpp - Kopete GroupWise Protocol
+
+ Copyright (c) 2004 SUSE Linux AG http://www.suse.com
+
+ Based on Testbed
+ Copyright (c) 2003 by Will Stephenson <[email protected]>
+ rtfizeTest from nm_rtfize_text, from Gaim src/protocols/novell/nmuser.c
+ Copyright (c) 2004 Novell, Inc. All Rights Reserved
+
+ Kopete (c) 2002-2003 by the Kopete developers <[email protected]>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+#include <qregexp.h>
+#include <qstringlist.h>
+
+#include <kgenericfactory.h>
+#include <kdebug.h>
+
+#include "kopeteaccountmanager.h"
+#include "kopeteonlinestatusmanager.h"
+#include "kopeteglobal.h"
+
+#include "gwaccount.h"
+#include "gwerror.h"
+#include "gwcontact.h"
+#include "gwprotocol.h"
+#include "ui/gwaddcontactpage.h"
+#include "ui/gweditaccountwidget.h"
+
+typedef KGenericFactory<GroupWiseProtocol> GroupWiseProtocolFactory;
+K_EXPORT_COMPONENT_FACTORY( kopete_groupwise, GroupWiseProtocolFactory( "kopete_groupwise" ) )
+
+GroupWiseProtocol *GroupWiseProtocol::s_protocol = 0L;
+
+GroupWiseProtocol::GroupWiseProtocol( QObject* parent, const char *name, const QStringList &/*args*/ )
+ : Kopete::Protocol( GroupWiseProtocolFactory::instance(), parent, name ),
+/* initialise Kopete::OnlineStatus that should be user selectable in the user interface */
+ groupwiseOffline ( Kopete::OnlineStatus::Offline, 0, this, GroupWise::Offline, QString::null,
+ i18n( "Offline" ), i18n( "O&ffline" ), Kopete::OnlineStatusManager::Offline ),
+ groupwiseAvailable ( Kopete::OnlineStatus::Online, 25, this, GroupWise::Available, QString::null,
+ i18n( "Online" ), i18n( "&Online" ), Kopete::OnlineStatusManager::Online ),
+ groupwiseBusy ( Kopete::OnlineStatus::Away, 18, this, GroupWise::Busy, "contact_busy_overlay",
+ i18n( "Busy" ), i18n( "&Busy" ), Kopete::OnlineStatusManager::Busy, Kopete::OnlineStatusManager::HasAwayMessage ),
+ groupwiseAway ( Kopete::OnlineStatus::Away, 20, this, GroupWise::Away, "contact_away_overlay",
+ i18n( "Away" ), i18n( "&Away" ), Kopete::OnlineStatusManager::Away, Kopete::OnlineStatusManager::HasAwayMessage ),
+ groupwiseAwayIdle ( Kopete::OnlineStatus::Away, 15, this, GroupWise::AwayIdle, "contact_away_overlay",
+ i18n( "Idle" ), "FIXME: Make groupwiseAwayIdle unselectable", Kopete::OnlineStatusManager::Idle,
+ Kopete::OnlineStatusManager::HideFromMenu ),
+ groupwiseAppearOffline( Kopete::OnlineStatus::Invisible, 2, this, 98, "contact_invisible_overlay",
+ i18n( "Appear Offline" ), i18n( "A&ppear Offline" ), Kopete::OnlineStatusManager::Invisible ),
+/* initialise Kopete::OnlineStatus used by the protocol, but that are not user selectable */
+ groupwiseUnknown ( Kopete::OnlineStatus::Unknown, 25, this, GroupWise::Unknown, "status_unknown",
+ i18n( "Unknown" ) ),
+ groupwiseInvalid ( Kopete::OnlineStatus::Unknown, 25, this, GroupWise::Invalid, "status_unknown",
+ i18n( "Invalid Status" ) ),
+ groupwiseConnecting ( Kopete::OnlineStatus::Connecting, 25, this, 99, "groupwise_connecting",
+ i18n( "Connecting" ) ),
+ propGivenName( Kopete::Global::Properties::self()->firstName() ),
+ propLastName( Kopete::Global::Properties::self()->lastName() ),
+ propFullName( Kopete::Global::Properties::self()->fullName() ),
+ propAwayMessage( Kopete::Global::Properties::self()->awayMessage() ),
+ propAutoReply( "groupwiseAutoReply", i18n( "Auto Reply Message" ), QString::null, false, false ),
+ propCN( "groupwiseCommonName", i18n( "Common Name" ), QString::null, true, false ),
+ propPhoneWork( Kopete::Global::Properties::self()->workPhone() ),
+ propPhoneMobile( Kopete::Global::Properties::self()->privateMobilePhone() ),
+ propEmail( Kopete::Global::Properties::self()->emailAddress() )
+{
+ // ^^ That is all member initialiser syntax, not broken indentation!
+ kdDebug( GROUPWISE_DEBUG_GLOBAL ) << k_funcinfo << endl;
+
+ s_protocol = this;
+
+ addAddressBookField( "messaging/groupwise", Kopete::Plugin::MakeIndexField );
+}
+
+GroupWiseProtocol::~GroupWiseProtocol()
+{
+}
+
+Kopete::Contact *GroupWiseProtocol::deserializeContact(
+ Kopete::MetaContact *metaContact, const QMap<QString, QString> &serializedData,
+ const QMap<QString, QString> &/* addressBookData */)
+{
+ QString dn = serializedData[ "DN" ];
+ QString accountId = serializedData[ "accountId" ];
+ QString displayName = serializedData[ "displayName" ];
+ int objectId = serializedData[ "objectId" ].toInt();
+ int parentId = serializedData[ "parentId" ].toInt();
+ int sequence = serializedData[ "sequenceNumber" ].toInt();
+
+ QDict<Kopete::Account> accounts = Kopete::AccountManager::self()->accounts( this );
+
+ Kopete::Account *account = accounts[ accountId ];
+ if ( !account )
+ {
+ kdDebug(GROUPWISE_DEBUG_GLOBAL) << "Account doesn't exist, skipping" << endl;
+ return 0;
+ }
+
+ // FIXME: creating a contact with a userId here
+ return new GroupWiseContact(account, dn, metaContact, objectId, parentId, sequence );
+}
+
+AddContactPage * GroupWiseProtocol::createAddContactWidget( QWidget *parent, Kopete::Account * account )
+{
+ kdDebug( GROUPWISE_DEBUG_GLOBAL ) << "Creating Add Contact Page" << endl;
+ return new GroupWiseAddContactPage( account, parent, "addcontactpage");
+}
+
+KopeteEditAccountWidget * GroupWiseProtocol::createEditAccountWidget( Kopete::Account *account, QWidget *parent )
+{
+ kdDebug(GROUPWISE_DEBUG_GLOBAL) << "Creating Edit Account Page" << endl;
+ return new GroupWiseEditAccountWidget( parent, account );
+}
+
+Kopete::Account *GroupWiseProtocol::createNewAccount( const QString &accountId )
+{
+ return new GroupWiseAccount( this, accountId );
+}
+
+GroupWiseProtocol *GroupWiseProtocol::protocol()
+{
+ return s_protocol;
+}
+
+Kopete::OnlineStatus GroupWiseProtocol::gwStatusToKOS( const int gwInternal )
+{
+ Kopete::OnlineStatus status;
+ switch ( gwInternal )
+ {
+ case GroupWise::Unknown:
+ status = groupwiseUnknown;
+ break;
+ case GroupWise::Offline:
+ status = groupwiseOffline;
+ break;
+ case GroupWise::Available:
+ status = groupwiseAvailable;
+ break;
+ case GroupWise::Busy:
+ status = groupwiseBusy;
+ break;
+ case GroupWise::Away:
+ status = groupwiseAway;
+ break;
+ case GroupWise::AwayIdle:
+ status = groupwiseAwayIdle;
+ break;
+ case GroupWise::Invalid:
+ status = groupwiseInvalid;
+ break;
+ default:
+ status = groupwiseInvalid;
+ kdWarning( GROUPWISE_DEBUG_GLOBAL ) << k_funcinfo << "Got unrecognised status value" << gwInternal << endl;
+ }
+ return status;
+}
+
+QString GroupWiseProtocol::rtfizeText( const QString & plain )
+{
+ // transcode a utf-8 encoded string into an rtf string
+ // iterate through the input string converting each char into the equivalent rtf
+ // of single-byte characters with first byte =< 0x7f (127), { } \ are escaped. \n are converted into \par , the rest are appended verbatim
+ // of multi-byte UTF-8 characters 2 to 6 bytes long (with first byte > 0x7f), these are recoded as 32 bit values, escaped as \u<val>? strings
+
+ // vanilla RTF "envelope" that doesn't say much but causes other clients to accept the message
+ QString rtfTemplate = QString::fromLatin1("{\\rtf1\\ansi\n"
+ "{\\fonttbl{\\f0\\fnil Unknown;}}\n"
+ "{\\colortbl ;\\red0\\green0\\blue0;}\n"
+ "\\uc1\\cf1\\f0\\fs18 %1\\par\n}");
+ QString outputText; // output text
+ QCString plainUtf8 = plain.utf8(); // encoded as UTF8, because that's what this encoding algorithm, taken from Gaim's Novell plugin
+ uint index = 0; // current char to transcode
+ while ( index < plainUtf8.length() )
+ {
+ Q_UINT8 current = plainUtf8.data()[ index ];
+ if ( current <= 0x7F )
+ {
+ switch ( current )
+ {
+ case '{':
+ case '}':
+ case '\\':
+ outputText.append( QString( "\\%1" ).arg( QChar( current ) ) );
+ break;
+ case '\n':
+ outputText.append( "\\par " );
+ break;
+ default:
+ outputText.append( QChar( current ) );
+ break;
+ }
+ ++index;
+ }
+ else
+ {
+ Q_UINT32 ucs4Char;
+ int bytesEncoded;
+ QString escapedUnicodeChar;
+ if ( current <= 0xDF )
+ {
+ ucs4Char = (( plainUtf8.data()[ index ] & 0x001F) << 6) |
+ ( plainUtf8.data()[ index+1 ] & 0x003F);
+ bytesEncoded = 2;
+ }
+ else if ( current <= 0xEF )
+ {
+ ucs4Char = (( plainUtf8.data()[ index ] & 0x000F) << 12) |
+ (( plainUtf8.data()[ index+1 ] & 0x003F) << 6) |
+ ( plainUtf8.data()[ index+2 ] & 0x003F);
+ bytesEncoded = 3;
+ }
+ else if ( current <= 0xF7 )
+ {
+ ucs4Char = (( plainUtf8.data()[ index ] & 0x0007) << 18) |
+ (( plainUtf8.data()[ index+1 ] & 0x003F) << 12) |
+ (( plainUtf8.data()[ index+2 ] & 0x003F) << 6) |
+ ( plainUtf8.data()[ index+3 ] & 0x003F);
+ bytesEncoded = 4;
+ }
+ else if ( current <= 0xFB )
+ {
+ ucs4Char = (( plainUtf8.data()[ index ] & 0x0003) << 24 ) |
+ (( plainUtf8.data()[ index+1 ] & 0x003F) << 18) |
+ (( plainUtf8.data()[ index+2 ] & 0x003F) << 12) |
+ (( plainUtf8.data()[ index+3 ] & 0x003F) << 6) |
+ ( plainUtf8.data()[ index+4 ] & 0x003F);
+ bytesEncoded = 5;
+ }
+ else if ( current <= 0xFD )
+ {
+ ucs4Char = (( plainUtf8.data()[ index ] & 0x0001) << 30 ) |
+ (( plainUtf8.data()[ index+1 ] & 0x003F) << 24) |
+ (( plainUtf8.data()[ index+2 ] & 0x003F) << 18) |
+ (( plainUtf8.data()[ index+3 ] & 0x003F) << 12) |
+ (( plainUtf8.data()[ index+4 ] & 0x003F) << 6) |
+ ( plainUtf8.data()[ index+5 ] & 0x003F);
+ bytesEncoded = 6;
+ }
+ else
+ {
+ kdDebug( GROUPWISE_DEBUG_GLOBAL ) << k_funcinfo << "bogus utf-8 lead byte: 0x" << QTextStream::hex << current << endl;
+ ucs4Char = 0x003F;
+ bytesEncoded = 1;
+ }
+ index += bytesEncoded;
+ escapedUnicodeChar = QString("\\u%1?").arg( ucs4Char );
+ kdDebug( GROUPWISE_DEBUG_GLOBAL ) << "unicode escaped char: " << escapedUnicodeChar << endl;
+ outputText.append( escapedUnicodeChar );
+ }
+ }
+ return rtfTemplate.arg( outputText );
+}
+
+QString GroupWiseProtocol::dnToDotted( const QString & dn )
+{
+ QRegExp rx("[a-zA-Z]*=(.*)$", false );
+ if( !dn.find( '=' ) ) // if it's not a DN, return it unprocessed
+ return dn;
+
+ // split the dn into elements
+ QStringList elements = QStringList::split( ',', dn );
+ // remove the key, keep the value
+ for ( QStringList::Iterator it = elements.begin(); it != elements.end(); ++it )
+ {
+ if ( rx.search( *it ) != -1 )
+ *it = rx.cap( 1 );
+ }
+ QString dotted = elements.join( "." );
+ // reassemble as dotted
+
+ return dotted;
+}
+#include "gwprotocol.moc"
diff --git a/kopete/protocols/groupwise/gwprotocol.h b/kopete/protocols/groupwise/gwprotocol.h
new file mode 100644
index 00000000..c3271f18
--- /dev/null
+++ b/kopete/protocols/groupwise/gwprotocol.h
@@ -0,0 +1,112 @@
+/*
+ gwprotocol.h - Kopete GroupWise Protocol
+
+ Copyright (c) 2004 SUSE Linux AG http://www.suse.com
+
+ Based on Testbed
+ Copyright (c) 2003 by Will Stephenson <[email protected]>
+ rtfizeTest from nm_rtfize_text, from Gaim src/protocols/novell/nmuser.c
+ Copyright (c) 2004 Novell, Inc. All Rights Reserved
+
+ Kopete (c) 2002-2003 by the Kopete developers <[email protected]>
+
+ *************************************************************************
+ * *
+ * This library 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 TESTBEDPROTOCOL_H
+#define TESTBEDPROTOCOL_H
+
+#include <kopeteprotocol.h>
+#include "kopetecontactproperty.h"
+#include "kopeteonlinestatus.h"
+
+/**
+ * Encapsulates the generic actions associated with this protocol
+ * @author Will Stephenson
+ */
+class GroupWiseProtocol : public Kopete::Protocol
+{
+ Q_OBJECT
+public:
+ GroupWiseProtocol(QObject *parent, const char *name, const QStringList &args);
+ ~GroupWiseProtocol();
+ /**
+ * Convert the serialised data back into a GroupWiseContact and add this
+ * to its Kopete::MetaContact
+ */
+ virtual Kopete::Contact *deserializeContact(
+ Kopete::MetaContact *metaContact,
+ const QMap< QString, QString > & serializedData,
+ const QMap< QString, QString > & addressBookData
+ );
+ /**
+ * Generate the widget needed to add GroupWiseContacts
+ */
+ virtual AddContactPage * createAddContactWidget( QWidget *parent, Kopete::Account *account );
+ /**
+ * Generate the widget needed to add/edit accounts for this protocol
+ */
+ virtual KopeteEditAccountWidget * createEditAccountWidget( Kopete::Account *account, QWidget *parent );
+ /**
+ * Generate a GroupWiseAccount
+ */
+ virtual Kopete::Account * createNewAccount( const QString &accountId );
+ /**
+ * Access the instance of this protocol
+ */
+ static GroupWiseProtocol *protocol();
+ /**
+ * Transform a GroupWise internal status into a Kopete::OnlineStatus
+ */
+ Kopete::OnlineStatus gwStatusToKOS( const int gwInternal );
+ /**
+ * Wrap unformatted text in RTF formatting so that other GroupWise clients will display it
+ * @param plain unformatted text
+ * @return RTF text (in UCS-4 encoding)
+ */
+ QString rtfizeText( const QString & plain );
+ /**
+ * Convert full DNs to dotted-untyped format
+ * Assumes the DN is normalised - comma separated, no spaces between elements
+ * eg cn=wstephenson,o=suse becomes wstephenson.suse
+ */
+ static QString dnToDotted( const QString & dn );
+ /**
+ * Online statuses used for contacts' presence
+ */
+ const Kopete::OnlineStatus groupwiseOffline;
+ const Kopete::OnlineStatus groupwiseAvailable;
+ const Kopete::OnlineStatus groupwiseBusy;
+ const Kopete::OnlineStatus groupwiseAway;
+ const Kopete::OnlineStatus groupwiseAwayIdle;
+ const Kopete::OnlineStatus groupwiseAppearOffline;
+ const Kopete::OnlineStatus groupwiseUnknown;
+ const Kopete::OnlineStatus groupwiseInvalid;
+ const Kopete::OnlineStatus groupwiseConnecting;
+
+ /**
+ * Contact properties
+ */
+ const Kopete::ContactPropertyTmpl propGivenName;
+ const Kopete::ContactPropertyTmpl propLastName;
+ const Kopete::ContactPropertyTmpl propFullName;
+ const Kopete::ContactPropertyTmpl propAwayMessage;
+ const Kopete::ContactPropertyTmpl propAutoReply;
+ const Kopete::ContactPropertyTmpl propCN;
+ const Kopete::ContactPropertyTmpl propPhoneWork;
+ const Kopete::ContactPropertyTmpl propPhoneMobile;
+ const Kopete::ContactPropertyTmpl propEmail;
+
+
+protected:
+ static GroupWiseProtocol *s_protocol;
+};
+
+#endif
diff --git a/kopete/protocols/groupwise/icons/Makefile.am b/kopete/protocols/groupwise/icons/Makefile.am
new file mode 100644
index 00000000..224eb420
--- /dev/null
+++ b/kopete/protocols/groupwise/icons/Makefile.am
@@ -0,0 +1,3 @@
+kopeteicondir = $(kde_datadir)/kopete/icons
+kopeteicon_ICON = AUTO
+
diff --git a/kopete/protocols/groupwise/icons/cr16-action-groupwise_away.png b/kopete/protocols/groupwise/icons/cr16-action-groupwise_away.png
new file mode 100644
index 00000000..43d03896
--- /dev/null
+++ b/kopete/protocols/groupwise/icons/cr16-action-groupwise_away.png
Binary files differ
diff --git a/kopete/protocols/groupwise/icons/cr16-action-groupwise_busy.png b/kopete/protocols/groupwise/icons/cr16-action-groupwise_busy.png
new file mode 100644
index 00000000..1ddd82b6
--- /dev/null
+++ b/kopete/protocols/groupwise/icons/cr16-action-groupwise_busy.png
Binary files differ
diff --git a/kopete/protocols/groupwise/icons/cr16-action-groupwise_connecting.mng b/kopete/protocols/groupwise/icons/cr16-action-groupwise_connecting.mng
new file mode 100644
index 00000000..e2a10ead
--- /dev/null
+++ b/kopete/protocols/groupwise/icons/cr16-action-groupwise_connecting.mng
Binary files differ
diff --git a/kopete/protocols/groupwise/icons/cr16-action-groupwise_invisible.png b/kopete/protocols/groupwise/icons/cr16-action-groupwise_invisible.png
new file mode 100644
index 00000000..ee9b37f1
--- /dev/null
+++ b/kopete/protocols/groupwise/icons/cr16-action-groupwise_invisible.png
Binary files differ
diff --git a/kopete/protocols/groupwise/icons/cr16-action-groupwise_online.png b/kopete/protocols/groupwise/icons/cr16-action-groupwise_online.png
new file mode 100644
index 00000000..87dcf0dc
--- /dev/null
+++ b/kopete/protocols/groupwise/icons/cr16-action-groupwise_online.png
Binary files differ
diff --git a/kopete/protocols/groupwise/icons/cr16-action-logging.png b/kopete/protocols/groupwise/icons/cr16-action-logging.png
new file mode 100644
index 00000000..3d7b72fb
--- /dev/null
+++ b/kopete/protocols/groupwise/icons/cr16-action-logging.png
Binary files differ
diff --git a/kopete/protocols/groupwise/icons/cr16-app-groupwise_protocol.png b/kopete/protocols/groupwise/icons/cr16-app-groupwise_protocol.png
new file mode 100644
index 00000000..87dcf0dc
--- /dev/null
+++ b/kopete/protocols/groupwise/icons/cr16-app-groupwise_protocol.png
Binary files differ
diff --git a/kopete/protocols/groupwise/icons/cr22-action-logging.png b/kopete/protocols/groupwise/icons/cr22-action-logging.png
new file mode 100644
index 00000000..270ce62d
--- /dev/null
+++ b/kopete/protocols/groupwise/icons/cr22-action-logging.png
Binary files differ
diff --git a/kopete/protocols/groupwise/icons/cr22-app-groupwise_protocol.png b/kopete/protocols/groupwise/icons/cr22-app-groupwise_protocol.png
new file mode 100644
index 00000000..b2217573
--- /dev/null
+++ b/kopete/protocols/groupwise/icons/cr22-app-groupwise_protocol.png
Binary files differ
diff --git a/kopete/protocols/groupwise/icons/cr32-action-logging.png b/kopete/protocols/groupwise/icons/cr32-action-logging.png
new file mode 100644
index 00000000..0c228e11
--- /dev/null
+++ b/kopete/protocols/groupwise/icons/cr32-action-logging.png
Binary files differ
diff --git a/kopete/protocols/groupwise/icons/cr32-app-groupwise_protocol.png b/kopete/protocols/groupwise/icons/cr32-app-groupwise_protocol.png
new file mode 100644
index 00000000..20c6764e
--- /dev/null
+++ b/kopete/protocols/groupwise/icons/cr32-app-groupwise_protocol.png
Binary files differ
diff --git a/kopete/protocols/groupwise/icons/cr48-action-logging.png b/kopete/protocols/groupwise/icons/cr48-action-logging.png
new file mode 100644
index 00000000..0d348816
--- /dev/null
+++ b/kopete/protocols/groupwise/icons/cr48-action-logging.png
Binary files differ
diff --git a/kopete/protocols/groupwise/icons/cr48-app-groupwise_protocol.png b/kopete/protocols/groupwise/icons/cr48-app-groupwise_protocol.png
new file mode 100644
index 00000000..cf209bc6
--- /dev/null
+++ b/kopete/protocols/groupwise/icons/cr48-app-groupwise_protocol.png
Binary files differ
diff --git a/kopete/protocols/groupwise/icons/cr64-action-logging.png b/kopete/protocols/groupwise/icons/cr64-action-logging.png
new file mode 100644
index 00000000..7bb0ef56
--- /dev/null
+++ b/kopete/protocols/groupwise/icons/cr64-action-logging.png
Binary files differ
diff --git a/kopete/protocols/groupwise/icons/cr64-app-groupwise_protocol.png b/kopete/protocols/groupwise/icons/cr64-app-groupwise_protocol.png
new file mode 100644
index 00000000..75b7c634
--- /dev/null
+++ b/kopete/protocols/groupwise/icons/cr64-app-groupwise_protocol.png
Binary files differ
diff --git a/kopete/protocols/groupwise/kopete_groupwise.desktop b/kopete/protocols/groupwise/kopete_groupwise.desktop
new file mode 100644
index 00000000..6a692cf4
--- /dev/null
+++ b/kopete/protocols/groupwise/kopete_groupwise.desktop
@@ -0,0 +1,60 @@
+[Desktop Entry]
+Type=Service
+X-Kopete-Version=1000900
+Icon=groupwise_protocol
+ServiceTypes=Kopete/Protocol
+X-KDE-Library=kopete_groupwise
+X-Kopete-Messaging-Protocol=messaging/groupwise
+X-KDE-PluginInfo-Author=Will Stephenson
+X-KDE-PluginInfo-Name=kopete_groupwise
+X-KDE-PluginInfo-Version=0.8.0
+X-KDE-PluginInfo-Website=http://kopete.kde.org
+X-KDE-PluginInfo-Category=Protocols
+X-KDE-PluginInfo-Depends=
+X-KDE-PluginInfo-License=GPL
+X-KDE-PluginInfo-EnabledByDefault=false
+Name=GroupWise
+Name[bn]=গ্রুপ-ওয়াইজ
+Name[fa]=گروه هوشیار
+Name[ne]=समूहगत
+Name[ta]=குழு படி
+Name[tr]=Akıllı Gruplama
+Comment=Novell GroupWise Messenger
+Comment[bn]=নোভেল গ্রুপ-ওয়াইজ বার্তাবাহক
+Comment[cs]=Novell GroupWise komunikátor
+Comment[de]=Novell GroupWise-Nachrichtendienst
+Comment[es]=Mensajería GroupWise de Novell
+Comment[fa]=پیام‌رسان GroupWise ناول
+Comment[fi]=Novell GroupWise -viestijä
+Comment[fr]=Messagerie GroupWise Novell
+Comment[he]=תוכנת המסרים של נובל GroupWise
+Comment[is]=Novell GroupWise skilaboðaþjónustan
+Comment[it]=Messaggistica per GroupWise di Novell
+Comment[ja]=Novell GroupWise メッセンジャー
+Comment[ka]= Novell GroupWise მყისიერი შეტყობინებების მაცნე
+Comment[kk]=Novell GroupWise хабарласуы
+Comment[km]=កម្មវិធី​ផ្ញើ​សារ​របស់​ណូវែល - GroupWise
+Comment[lt]=Novell GroupWise žinučių klientas
+Comment[mk]=Гласник за Novell GroupWise
+Comment[nb]=Novell GroupWise meldingsprogram
+Comment[nds]=Novell GroupWise-Kortnarichtendeenst
+Comment[ne]=नोभेल समूहगत मेसेन्जर
+Comment[nl]=Protocol voor Novell GroupWise Messenger
+Comment[nn]=Lynmeldingsprogrammet Novell GroupWise
+Comment[pl]=Komunikator Novell GroupWise
+Comment[pt_BR]=Mensageiro do GroupWise da Novell
+Comment[ru]=Программа обмена сообщениями Novell GroupWise
+Comment[se]=Novell GroupWise-šleađgadieđáhusprográmma
+Comment[sl]=Sporočilnik za Novell GroupWise
+Comment[sr]=Novell-ов гласник GroupWise
+Comment[sr@Latn]=Novell-ov glasnik GroupWise
+Comment[sv]=Novell GroupWise-meddelandeklient
+Comment[ta]=Novell GroupWise உடனடி தூதர்
+Comment[tg]=Пайёмбари Novell GroupWise
+Comment[tr]=Novell Akıllı Grup İletişimi
+Comment[uk]=Програма обміну повідомленнями Novell GroupWise
+Comment[uz]=Novell GroupWise mesenjer
+Comment[uz@cyrillic]=Novell GroupWise месенжер
+Comment[zh_CN]=Novell GroupWise 信使
+Comment[zh_TW]=Novell GroupWise 即時訊息
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 <[email protected]>
+
+ *************************************************************************
+ * *
+ * 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 <[email protected]>
+
+ *************************************************************************
+ * *
+ * 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 <[email protected]>
+
+ *************************************************************************
+ * *
+ * 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 <[email protected]>
+
+ *************************************************************************
+ * *
+ * 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 <[email protected]>
+
+ *************************************************************************
+ * *
+ * 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 <[email protected]>
+
+ *************************************************************************
+ * *
+ * 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 <[email protected]>
+
+ *************************************************************************
+ * *
+ * 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 <[email protected]>
+
+ *************************************************************************
+ * *
+ * 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 <[email protected]>
+
+ *************************************************************************
+ * *
+ * 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 <[email protected]>
+
+ *************************************************************************
+ * *
+ * 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 <[email protected]>
+
+ *************************************************************************
+ * *
+ * 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 <[email protected]>
+
+ *************************************************************************
+ * *
+ * 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 <[email protected]>
+
+ *************************************************************************
+ * *
+ * 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 <[email protected]>
+
+ *************************************************************************
+ * *
+ * 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 <[email protected]>
+
+ *************************************************************************
+ * *
+ * 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 <[email protected]>
+
+ *************************************************************************
+ * *
+ * 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 <[email protected]>
+
+ *************************************************************************
+ * *
+ * 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 <[email protected]>
+
+ Kopete (c) 2002-2004 by the Kopete developers <[email protected]>
+
+ *************************************************************************
+ * *
+ * 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 <[email protected]>
+
+ Kopete (c) 2002-2004 by the Kopete developers <[email protected]>
+
+ *************************************************************************
+ * *
+ * 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 <[email protected]>
+
+ *************************************************************************
+ * *
+ * 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 <[email protected]>
+
+ *************************************************************************
+ * *
+ * 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 <[email protected]>
+
+ *************************************************************************
+ * *
+ * 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 <[email protected]>
+
+ *************************************************************************
+ * *
+ * 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 <[email protected]>
+
+ *************************************************************************
+ * *
+ * 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 <[email protected]>
+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 <[email protected]>
+
+ *************************************************************************
+ * *
+ * 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 <[email protected]>
+
+ *************************************************************************
+ * *
+ * 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 <[email protected]>
+
+ *************************************************************************
+ * *
+ * 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 <[email protected]>
+
+ *************************************************************************
+ * *
+ * 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 <[email protected]>
+
+ *************************************************************************
+ * *
+ * 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 <[email protected]>
+
+ *************************************************************************
+ * *
+ * 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 <[email protected]>
+
+ *************************************************************************
+ * *
+ * 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 <[email protected]>
+
+ *************************************************************************
+ * *
+ * 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 <[email protected]>
+
+ *************************************************************************
+ * *
+ * 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, &currentList) )
+ {
+ 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 <[email protected]>
+
+ *************************************************************************
+ * *
+ * 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 <[email protected]> (original code)
+ Copyright (c) 2004 by Thiago S. Barcelos <[email protected]> (Kopete port)
+ Kopete (c) 2002-2003 by the Kopete developers <[email protected]>
+
+ *************************************************************************
+ * *
+ * 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("&"), "&amp;");
+ str.replace(QRegExp("<"), "&lt;");
+ str.replace(QRegExp(">"), "&gt;");
+ str.replace(QRegExp("\""), "&quot;");
+ 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 += "&nbsp;";
+ 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 <[email protected]> (original code)
+ Copyright (c) 2004 by Thiago S. Barcelos <[email protected]> (Kopete port)
+ Kopete (c) 2002-2003 by the Kopete developers <[email protected]>
+
+ *************************************************************************
+ * *
+ * 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("&"), "&amp;");
+ str.replace(QRegExp("<"), "&lt;");
+ str.replace(QRegExp(">"), "&gt;");
+ str.replace(QRegExp("\""), "&quot;");
+ 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 += "&nbsp;";
+ 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 <[email protected]> (original code)
+ Copyright (c) 2004 by Thiago S. Barcelos <[email protected]> (Kopete port)
+ Kopete (c) 2002-2003 by the Kopete developers <[email protected]>
+
+ *************************************************************************
+ * *
+ * 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 <[email protected]>
+
+ *************************************************************************
+ * *
+ * 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 <[email protected]>
+
+ *************************************************************************
+ * *
+ * 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 <[email protected]>
+
+ *************************************************************************
+ * *
+ * 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 <[email protected]>
+
+ *************************************************************************
+ * *
+ * 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 <[email protected]>
+
+ *************************************************************************
+ * *
+ * 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 <[email protected]>
+
+ *************************************************************************
+ * *
+ * 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 <[email protected]>
+
+ *************************************************************************
+ * *
+ * 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 <[email protected]>
+
+ *************************************************************************
+ * *
+ * 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 <[email protected]>
+
+ *************************************************************************
+ * *
+ * 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 <[email protected]>
+
+ *************************************************************************
+ * *
+ * 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 <[email protected]>
+
+ *************************************************************************
+ * *
+ * 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 <[email protected]>
+
+ *************************************************************************
+ * *
+ * 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 <[email protected]>
+
+ *************************************************************************
+ * *
+ * 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 <[email protected]>
+
+ *************************************************************************
+ * *
+ * 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 <[email protected]>
+
+ *************************************************************************
+ * *
+ * 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 <[email protected]>
+
+ *************************************************************************
+ * *
+ * 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 <[email protected]>
+
+ *************************************************************************
+ * *
+ * 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 <[email protected]>
+
+ *************************************************************************
+ * *
+ * 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 <[email protected]>
+
+ *************************************************************************
+ * *
+ * 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 <[email protected]>
+
+ *************************************************************************
+ * *
+ * 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 <[email protected]>
+
+ *************************************************************************
+ * *
+ * 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 <[email protected]>
+
+ *************************************************************************
+ * *
+ * 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 <[email protected]>
+
+ *************************************************************************
+ * *
+ * 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 <[email protected]>
+
+ *************************************************************************
+ * *
+ * 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 <[email protected]>
+
+ *************************************************************************
+ * *
+ * 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 <[email protected]>
+
+ *************************************************************************
+ * *
+ * 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 <[email protected]>
+
+ *************************************************************************
+ * *
+ * 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 <[email protected]>
+
+ *************************************************************************
+ * *
+ * 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 <[email protected]>
+
+ *************************************************************************
+ * *
+ * 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 <[email protected]>
+
+ *************************************************************************
+ * *
+ * 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 <[email protected]>
+
+ *************************************************************************
+ * *
+ * 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 <[email protected]>
+
+ *************************************************************************
+ * *
+ * 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 <[email protected]>
+
+ *************************************************************************
+ * *
+ * 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 <[email protected]>
+
+ *************************************************************************
+ * *
+ * 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 <[email protected]>
+
+ *************************************************************************
+ * *
+ * 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 <[email protected]>
+
+ *************************************************************************
+ * *
+ * 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 <[email protected]>
+
+ *************************************************************************
+ * *
+ * 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 <[email protected]>
+
+ *************************************************************************
+ * *
+ * 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 <[email protected]>
+
+ *************************************************************************
+ * *
+ * 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 <[email protected]>
+
+ *************************************************************************
+ * *
+ * 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 <[email protected]>
+
+ *************************************************************************
+ * *
+ * 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 <[email protected]>
+
+ *************************************************************************
+ * *
+ * 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 <[email protected]>
+
+ *************************************************************************
+ * *
+ * 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 <[email protected]>
+
+ *************************************************************************
+ * *
+ * 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 <[email protected]>
+
+ *************************************************************************
+ * *
+ * 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 <[email protected]>
+
+ *************************************************************************
+ * *
+ * 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 <[email protected]>
+
+ *************************************************************************
+ * *
+ * 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 <[email protected]>
+
+ *************************************************************************
+ * *
+ * 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 <[email protected]>
+
+ *************************************************************************
+ * *
+ * 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 <[email protected]>
+
+ *************************************************************************
+ * *
+ * 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 <[email protected]>
+
+ *************************************************************************
+ * *
+ * 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 <[email protected]>
+
+ *************************************************************************
+ * *
+ * 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 <[email protected]>
+
+ *************************************************************************
+ * *
+ * 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 <[email protected]>
+
+ *************************************************************************
+ * *
+ * 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 <[email protected]>
+
+ *************************************************************************
+ * *
+ * 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 <[email protected]>
+
+ *************************************************************************
+ * *
+ * 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 <[email protected]>
+
+ *************************************************************************
+ * *
+ * 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 <[email protected]>
+
+ *************************************************************************
+ * *
+ * 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 <[email protected]>
+
+ *************************************************************************
+ * *
+ * 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 <[email protected]>
+
+ *************************************************************************
+ * *
+ * 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 <[email protected]>
+
+ *************************************************************************
+ * *
+ * 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 <[email protected]>
+
+ *************************************************************************
+ * *
+ * 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 <[email protected]>
+
+ *************************************************************************
+ * *
+ * 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 <[email protected]>
+
+ *************************************************************************
+ * *
+ * 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 <[email protected]>
+
+ *************************************************************************
+ * *
+ * 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 <[email protected]>
+
+ *************************************************************************
+ * *
+ * 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 <[email protected]>
+
+ *************************************************************************
+ * *
+ * 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 <[email protected]>
+
+ *************************************************************************
+ * *
+ * 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 <[email protected]>, (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 <[email protected]>
+
+ *************************************************************************
+ * *
+ * 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 <[email protected]>
+
+ *************************************************************************
+ * *
+ * 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 <[email protected]>
+
+ *************************************************************************
+ * *
+ * 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 <[email protected]>
+
+ *************************************************************************
+ * *
+ * 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 <[email protected]>
+
+ *************************************************************************
+ * *
+ * 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 <[email protected]>
+
+ *************************************************************************
+ * *
+ * 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 <[email protected]>
+
+ *************************************************************************
+ * *
+ * 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 <[email protected]>
+
+ *************************************************************************
+ * *
+ * 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 <[email protected]>, (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 <[email protected]>, (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, "[email protected]" ) );
+ 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 <[email protected]>
+
+ *************************************************************************
+ * *
+ * 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 <[email protected]>
+
+ *************************************************************************
+ * *
+ * 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 <[email protected]>
+
+ *************************************************************************
+ * *
+ * 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 <[email protected]>
+
+ *************************************************************************
+ * *
+ * 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 <[email protected]>
+
+ *************************************************************************
+ * *
+ * 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 <[email protected]>
+
+ *************************************************************************
+ * *
+ * 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 <[email protected]>
+
+ *************************************************************************
+ * *
+ * 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 <[email protected]>
+
+ *************************************************************************
+ * *
+ * 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 <[email protected]>
+
+ *************************************************************************
+ * *
+ * 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 <[email protected]>
+
+ *************************************************************************
+ * *
+ * 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
diff --git a/kopete/protocols/groupwise/ui/Makefile.am b/kopete/protocols/groupwise/ui/Makefile.am
new file mode 100644
index 00000000..3cd76e27
--- /dev/null
+++ b/kopete/protocols/groupwise/ui/Makefile.am
@@ -0,0 +1,19 @@
+INCLUDES = -I$(top_srcdir)/protocols/groupwise/libgroupwise \
+ -I$(top_srcdir)/kopete/protocols/groupwise/libgroupwise -I$(top_srcdir)/kopete/protocols/groupwise/libgroupwise/qca/src
+METASOURCES = AUTO
+noinst_LTLIBRARIES = libkopetegroupwiseui.la
+AM_CPPFLAGS = $(KOPETE_INCLUDES) \
+ -I$(srcdir)/..\
+ -I$(srcdir)/../libgroupwise \
+ $(all_includes)
+
+libkopetegroupwiseui_la_LDFLAGS = $(all_libraries)
+libkopetegroupwiseui_la_SOURCES = gwaccountpreferences.ui gwaddcontactpage.cpp \
+ gwaddui.ui gweditaccountwidget.cpp gwreceiveinvitationdialog.cpp \
+ gwshowinvitation.ui gwcontactpropswidget.ui gwcontactproperties.cpp gwprivacy.ui \
+ gwprivacydialog.cpp gwsearch.cpp gwcustomstatuswidget.ui gwcustomstatusedit.ui \
+ gwcontactsearch.ui gwchatsearchwidget.ui gwchatsearchdialog.cpp gwchatpropswidget.ui \
+ gwchatpropsdialog.cpp
+
+noinst_HEADERS = gwreceiveinvitationdialog.h gwcontactproperties.h \
+ gwprivacydialog.h gwsearch.h gwchatsearchdialog.h gwchatpropsdialog.h
diff --git a/kopete/protocols/groupwise/ui/gwaccountpreferences.ui b/kopete/protocols/groupwise/ui/gwaccountpreferences.ui
new file mode 100644
index 00000000..b5cfabcc
--- /dev/null
+++ b/kopete/protocols/groupwise/ui/gwaccountpreferences.ui
@@ -0,0 +1,320 @@
+<!DOCTYPE UI><UI version="3.3" stdsetdef="1">
+<class>GroupWiseAccountPreferences</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>GroupWiseAccountPreferences</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>366</width>
+ <height>404</height>
+ </rect>
+ </property>
+ <property name="caption">
+ <string>Account Preferences - Groupwise</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <widget class="QTabWidget">
+ <property name="name">
+ <cstring>tabWidget11</cstring>
+ </property>
+ <widget class="QWidget">
+ <property name="name">
+ <cstring>tab</cstring>
+ </property>
+ <attribute name="title">
+ <string>B&amp;asic Setup</string>
+ </attribute>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>groupBox55</cstring>
+ </property>
+ <property name="title">
+ <string>Account Information</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout1</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel1</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;User ID:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>m_userId</cstring>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>The account name of your account.</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>The account name of your account.</string>
+ </property>
+ </widget>
+ <widget class="QLineEdit">
+ <property name="name">
+ <cstring>m_userId</cstring>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>The account name of your account.</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>The account name of your account.</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="Kopete::UI::PasswordWidget">
+ <property name="name">
+ <cstring>m_password</cstring>
+ </property>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>m_autoConnect</cstring>
+ </property>
+ <property name="text">
+ <string>E&amp;xclude from connect all</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Check to disable automatic connection. If checked, you may connect to this account manually using the icon in the bottom of the main Kopete window</string>
+ </property>
+ </widget>
+ <widget class="Line">
+ <property name="name">
+ <cstring>line1</cstring>
+ </property>
+ <property name="frameShape">
+ <enum>HLine</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>Sunken</enum>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout66</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>labelServer</cstring>
+ </property>
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>5</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Ser&amp;ver:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>m_server</cstring>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>The IP address or hostname of the server you would like to connect to.</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>The IP address or hostname of the server you would like to connect to (for example im.yourcorp.com).</string>
+ </property>
+ </widget>
+ <widget class="QLineEdit">
+ <property name="name">
+ <cstring>m_server</cstring>
+ </property>
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>The IP address or hostname of the server you would like to connect to.</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>The IP address or hostname of the server you would like to connect to (for example im.yourcorp.com).</string>
+ </property>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>labelPort</cstring>
+ </property>
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>5</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Po&amp;rt:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>m_port</cstring>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>The port on the server that you would like to connect to.</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>The port on the server that you would like to connect to (default is 5222).</string>
+ </property>
+ </widget>
+ <widget class="QSpinBox">
+ <property name="name">
+ <cstring>m_port</cstring>
+ </property>
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="buttonSymbols">
+ <enum>UpDownArrows</enum>
+ </property>
+ <property name="maxValue">
+ <number>65535</number>
+ </property>
+ <property name="minValue">
+ <number>1</number>
+ </property>
+ <property name="value">
+ <number>8300</number>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>The port on the server that you would like to connect to.</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>The port on the server that you would like to connect to (default is 5222).</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ </vbox>
+ </widget>
+ </vbox>
+ </widget>
+ <widget class="QWidget">
+ <property name="name">
+ <cstring>TabPage</cstring>
+ </property>
+ <attribute name="title">
+ <string>Advanced &amp;Options</string>
+ </attribute>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>m_alwaysAccept</cstring>
+ </property>
+ <property name="text">
+ <string>A&amp;lways accept invitations</string>
+ </property>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer7</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>91</height>
+ </size>
+ </property>
+ </spacer>
+ </vbox>
+ </widget>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>labelStatusMessage</cstring>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ <property name="alignment">
+ <set>AlignCenter</set>
+ </property>
+ </widget>
+ </vbox>
+</widget>
+<customwidgets>
+ <customwidget>
+ <class>Kopete::UI::PasswordWidget</class>
+ <header location="local">kopetepasswordwidget.h</header>
+ <sizehint>
+ <width>50</width>
+ <height>50</height>
+ </sizehint>
+ <container>0</container>
+ <sizepolicy>
+ <hordata>1</hordata>
+ <verdata>0</verdata>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ <pixmap>image0</pixmap>
+ <signal>changed()</signal>
+ </customwidget>
+</customwidgets>
+<images>
+ <image name="image0">
+ <data format="PNG" length="866">89504e470d0a1a0a0000000d4948445200000016000000160806000000c4b46c3b0000032949444154388db59531681b6714c77f32373c8186ef0305eea005093258900eca26d30e3174a8a807d1c9ee940e5d4a276f09a414e22974ee609a4c75a0857a70a20c199ce93424e43414aee0c26910dc8105f7410df706413a7c915551db049a3e38b87bf7bedffddfc7ff7d578be398456c6c6cbce13d441cc7b5da02fcf4e8e99bde7a8f899b501515d959f64e10e71cd949c6e8d508e6cb7cb050fae49727444d87ed08a566dc0cea545a621b96725e62c522f312c4929ff9e7725e6203439282ec0bc72f74150c30c927d89690163f539619a044564973a1980ae54c01c136a1db518a0024808942780dead16a27e7e0ca55949a81668023b242fcd2901c394663072cd408ad75e18b6d43a7076143710aa1b9049ccd326e064a5979e8f0191cfc5878544368af1b24807caa4cfe507ef8aea0bf6dd8b92de7f00bc1562c95e64416e297f216aadcfa3ca43f10da1f8243112286871507fb05c3c7059d568bde96c5885b01af2d6e4a2db10dc8ff128e0fdd39f4cbaf8576dbe170702afcf6b86467bbce57df8680f0d3230767e0e62bdc55c5e53c476742fabbc318437f209886c3cd41d4b0f74049c78ef21476ef5846cf7ded2831848d55f0aa62816caade11adb7ed2fa0f71ce9d8619ac2e627824a45a72b00e413c5a95c0cf63e052bbe2014bfa738c3de3d251dfb0f8a80fda04e6480600113cc558a11a0e10b93a9225886cff04a8d10868662eab87f37271e59f2136f85a855bfda15f9594eb7a3b4ae0b933f95e161c5ceed88f254e97f2ad49b75eedf8562e2d8fb264355314da1dbada866abe47fedb106d01f78b71fec170c8f7276ef58da3de8f64a76bf6f634283730e9d2b9b8390ce0dae565c6a8e04b0710b746678f8a8e0e18382d173a1d7151c909fe4e84ccf57be3e76245b115143584ee73f27afc8e80b4c667e4c37b7054c8be1afde0de978a9c63485fea0457cec70aa089015ab9297e0938c240573cdb7651a4a7f20f43feb304a72aac2e73bd723da1fe5746ec0682bc26070f38c345905d7e238f6077c00dd8f85280211fcd91af84b02ef94a50c004502c1394813252f14575ca09839242f9484cb42df31e763edd237ff31d6c0ffa3fe17f0fb86c7715cfb1ba8bd86cc8d2decd30000000049454e44ae426082</data>
+ </image>
+</images>
+<layoutdefaults spacing="6" margin="11"/>
+</UI>
diff --git a/kopete/protocols/groupwise/ui/gwaddcontactpage.cpp b/kopete/protocols/groupwise/ui/gwaddcontactpage.cpp
new file mode 100644
index 00000000..93616f95
--- /dev/null
+++ b/kopete/protocols/groupwise/ui/gwaddcontactpage.cpp
@@ -0,0 +1,108 @@
+/*
+ Kopete GroupWise Protocol
+ gweditaccountwidget.cpp - widget for adding GroupWise contacts
+
+ Copyright (c) 2004 SUSE Linux AG http://www.suse.com
+
+ Based on Testbed
+ Copyright (c) 2003 by Will Stephenson <[email protected]>
+
+ Kopete (c) 2002-2003 by the Kopete developers <[email protected]>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "gwaddcontactpage.h"
+
+//#include <qcombobox.h>
+#include <qlabel.h>
+#include <qlayout.h>
+#include <qlineedit.h>
+#include <qlistview.h>
+#include <qpushbutton.h>
+#include <qradiobutton.h>
+#include <qtabwidget.h>
+#include <qvaluelist.h>
+
+#include <kdebug.h>
+#include <klocale.h>
+
+#include "kopeteaccount.h"
+#include "kopetemetacontact.h"
+
+#include "client.h"
+#include "gwaccount.h"
+#include "gwerror.h"
+//#include "gwprotocol.h"
+#include "gwsearch.h"
+#include "gwaddui.h"
+#include "userdetailsmanager.h"
+
+GroupWiseAddContactPage::GroupWiseAddContactPage( Kopete::Account * owner, QWidget* parent, const char* name )
+ : AddContactPage(parent, name)
+{
+ m_account = static_cast<GroupWiseAccount *>( owner );
+ kdDebug(GROUPWISE_DEBUG_GLOBAL) << k_funcinfo << endl;
+ ( new QVBoxLayout( this ) )->setAutoAdd( true );
+ if (owner->isConnected ())
+ {
+ m_searchUI = new GroupWiseContactSearch( m_account, QListView::Single, false,
+ this, "acwsearchwidget" );
+ show();
+ m_canadd = true;
+ }
+ else
+ {
+ m_noaddMsg1 = new QLabel (i18n ("You need to be connected to be able to add contacts."), this);
+ m_noaddMsg2 = new QLabel (i18n ("Connect to GroupWise Messenger and try again."), this);
+ m_canadd = false;
+ }
+}
+
+GroupWiseAddContactPage::~GroupWiseAddContactPage()
+{
+// , i18n( "The search was cancelled" )
+// , i18n( "There was an error while carrying out your search. Please change your search terms or try again later." )
+// i18n( "There was an error while carrying out your search. Please change your search terms or try again later." )
+}
+
+bool GroupWiseAddContactPage::apply( Kopete::Account* account, Kopete::MetaContact* parentContact )
+{
+ if ( validateData() )
+ {
+ QString contactId;
+ QString displayName;
+
+ QValueList< ContactDetails > selected = m_searchUI->selectedResults();
+ if ( selected.count() == 1 )
+ {
+ ContactDetails dt = selected.first();
+ m_account->client()->userDetailsManager()->addDetails( dt );
+ contactId = dt.dn;
+ displayName = dt.givenName + " " + dt.surname;
+ }
+ else
+ return false;
+
+ return ( account->addContact ( contactId, parentContact, Kopete::Account::ChangeKABC ) );
+ }
+ else
+ return false;
+}
+
+bool GroupWiseAddContactPage::validateData()
+{
+ if ( m_canadd )
+ return ( m_searchUI->m_results->selectedItem() );
+ else
+ return false;
+}
+
+#include "gwaddcontactpage.moc"
diff --git a/kopete/protocols/groupwise/ui/gwaddcontactpage.h b/kopete/protocols/groupwise/ui/gwaddcontactpage.h
new file mode 100644
index 00000000..aa195edd
--- /dev/null
+++ b/kopete/protocols/groupwise/ui/gwaddcontactpage.h
@@ -0,0 +1,68 @@
+/*
+ Kopete GroupWise Protocol
+ gweditaccountwidget.h- widget for adding GroupWise contacts
+
+ Copyright (c) 2004 SUSE Linux AG http://www.suse.com
+
+ Based on Testbed
+ Copyright (c) 2003 by Will Stephenson <[email protected]>
+
+ Kopete (c) 2002-2003 by the Kopete developers <[email protected]>
+
+ *************************************************************************
+ * *
+ * This library 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 TESTBEDADDCONTACTPAGE_H
+#define TESTBEDADDCONTACTPAGE_H
+
+#include "gwerror.h"
+
+#include <addcontactpage.h>
+
+class QLabel;
+namespace Kopete { class Account; }
+namespace Kopete { class MetaContact; }
+class GroupWiseAccount;
+class GroupWiseAddUI;
+//TODO: change this to a wrapper around Contact Search and Chatroom Search
+class GroupWiseContactSearch;
+
+/**
+ * A page in the Add Contact Wizard
+ * @author Will Stephenson
+*/
+class GroupWiseAddContactPage : public AddContactPage
+{
+ Q_OBJECT
+public:
+ GroupWiseAddContactPage( Kopete::Account * owner, QWidget* parent = 0, const char* name = 0 );
+ ~GroupWiseAddContactPage();
+
+ /**
+ * Make a contact out of the entered data
+ */
+ virtual bool apply(Kopete::Account* a, Kopete::MetaContact* m);
+ /**
+ * Is the data correct?
+ */
+ virtual bool validateData();
+protected:
+ QValueList< GroupWise::ContactDetails > m_searchResults;
+ unsigned char searchOperation( int comboIndex );
+ GroupWiseAccount * m_account;
+ GroupWiseAddUI * m_gwAddUI;
+ //TODO: make wrapper
+ GroupWiseContactSearch * m_searchUI;
+ QLabel *m_noaddMsg1;
+ QLabel *m_noaddMsg2;
+ bool m_canadd;
+};
+
+#endif
diff --git a/kopete/protocols/groupwise/ui/gwaddui.ui b/kopete/protocols/groupwise/ui/gwaddui.ui
new file mode 100644
index 00000000..16859bef
--- /dev/null
+++ b/kopete/protocols/groupwise/ui/gwaddui.ui
@@ -0,0 +1,137 @@
+<!DOCTYPE UI><UI version="3.3" stdsetdef="1">
+<class>GroupWiseAddUI</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>GroupWiseAddUI</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>392</width>
+ <height>343</height>
+ </rect>
+ </property>
+ <property name="caption">
+ <string>TestbedAddUI</string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string></string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <widget class="QTabWidget">
+ <property name="name">
+ <cstring>m_tabWidget</cstring>
+ </property>
+ <widget class="QWidget">
+ <property name="name">
+ <cstring>tab</cstring>
+ </property>
+ <attribute name="title">
+ <string>&amp;Basic</string>
+ </attribute>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QButtonGroup">
+ <property name="name">
+ <cstring>bg_addMethod</cstring>
+ </property>
+ <property name="title">
+ <string>Add Using</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLineEdit" row="0" column="1">
+ <property name="name">
+ <cstring>m_userName</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>A full or partial name. Asterisks are ignored</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Type some or all of the contact's name. Matches will be shown below</string>
+ </property>
+ </widget>
+ <widget class="QRadioButton" row="1" column="0">
+ <property name="name">
+ <cstring>rb_userId</cstring>
+ </property>
+ <property name="text">
+ <string>User &amp;ID:</string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="QRadioButton" row="0" column="0">
+ <property name="name">
+ <cstring>rb_userName</cstring>
+ </property>
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="text">
+ <string>Userna&amp;me:</string>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="1" column="1">
+ <property name="name">
+ <cstring>m_userId</cstring>
+ </property>
+ <property name="focusPolicy">
+ <enum>StrongFocus</enum>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>A correct User ID</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Use this field to add a contact if you already know the user's exact User ID</string>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ </vbox>
+ </widget>
+ <widget class="QWidget">
+ <property name="name">
+ <cstring>tab</cstring>
+ </property>
+ <attribute name="title">
+ <string>Ad&amp;vanced</string>
+ </attribute>
+ </widget>
+ </widget>
+ </vbox>
+</widget>
+<connections>
+ <connection>
+ <sender>rb_userId</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>m_userId</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>rb_userName</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>m_userName</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+</connections>
+<layoutdefaults spacing="6" margin="11"/>
+</UI>
diff --git a/kopete/protocols/groupwise/ui/gwchatpropsdialog.cpp b/kopete/protocols/groupwise/ui/gwchatpropsdialog.cpp
new file mode 100644
index 00000000..eabb75ab
--- /dev/null
+++ b/kopete/protocols/groupwise/ui/gwchatpropsdialog.cpp
@@ -0,0 +1,122 @@
+/*
+ Kopete Groupwise Protocol
+ gwchatpropsdialog.h - dialog for viewing/modifying chat properties
+
+ Copyright (c) 2005 SUSE Linux AG http://www.suse.com
+
+ Kopete (c) 2002-2005 by the Kopete developers <[email protected]>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include <qcheckbox.h>
+#include <qlabel.h>
+#include <qlineedit.h>
+#include <qlistview.h>
+
+#include <kdebug.h>
+#include <kpushbutton.h>
+#include <klocale.h>
+#include "gwerror.h"
+#include "gwchatpropswidget.h"
+
+#include "gwchatpropsdialog.h"
+
+GroupWiseChatPropsDialog::GroupWiseChatPropsDialog( QWidget * parent, const char * name )
+ : KDialogBase( parent, name, false, i18n( "Chatroom properties" ),
+ KDialogBase::Ok|KDialogBase::Cancel, Ok, true ), m_dirty( false )
+{
+ initialise();
+}
+
+GroupWiseChatPropsDialog::GroupWiseChatPropsDialog( const GroupWise::Chatroom & room, bool readOnly,
+ QWidget * parent, const char * name )
+ : KDialogBase( parent, name, false, i18n( "Chatroom properties" ),
+ KDialogBase::Ok|KDialogBase::Cancel, Ok, true ), m_dirty( false )
+{
+ initialise();
+ m_widget->m_description->setText( room.description );
+ m_widget->m_displayName->setText( room.displayName );
+ m_widget->m_disclaimer->setText( room.disclaimer );
+ m_widget->m_owner->setText( room.ownerDN );
+ m_widget->m_query->setText( room.query );
+ m_widget->m_topic->setText( room.topic );
+ m_widget->m_archive->setChecked( room.archive );
+ m_widget->m_maxUsers->setText( QString::number( room.maxUsers ) );
+ m_widget->m_createdOn->setText( room.createdOn.toString() );
+ m_widget->m_creator->setText( room.creatorDN );
+
+ m_widget->m_chkRead->setChecked( room.chatRights & GroupWise::Chatroom::Read || room.chatRights & GroupWise::Chatroom::Write || room.chatRights & GroupWise::Chatroom::Owner );
+ m_widget->m_chkWrite->setChecked( room.chatRights & GroupWise::Chatroom::Write || room.chatRights & GroupWise::Chatroom::Owner );
+ m_widget->m_chkModify->setChecked( room.chatRights & GroupWise::Chatroom::Modify || room.chatRights & GroupWise::Chatroom::Owner );
+
+ if ( readOnly )
+ {
+ m_widget->m_description->setReadOnly( true );
+ m_widget->m_disclaimer->setReadOnly( true );
+ m_widget->m_owner->setReadOnly( true );
+ m_widget->m_query->setReadOnly( true );
+ m_widget->m_topic->setReadOnly( true );
+ m_widget->m_archive->setEnabled( false );
+ m_widget->m_maxUsers->setReadOnly( true );
+ m_widget->m_createdOn->setReadOnly( true );
+ m_widget->m_creator->setReadOnly( true );
+ m_widget->m_chkRead->setEnabled( false );
+ m_widget->m_chkWrite->setEnabled( false );
+ m_widget->m_chkModify->setEnabled( false );
+ m_widget->m_btnAddAcl->setEnabled( false );
+ m_widget->m_btnEditAcl->setEnabled( false );
+ m_widget->m_btnDeleteAcl->setEnabled( false );
+ }
+
+}
+
+void GroupWiseChatPropsDialog::initialise()
+{
+ kdDebug( GROUPWISE_DEBUG_GLOBAL ) << k_funcinfo << endl;
+ m_widget = new GroupWiseChatPropsWidget( this );
+ connect( m_widget->m_topic, SIGNAL( textChanged( const QString & ) ), SLOT( slotWidgetChanged() ) );
+ connect( m_widget->m_owner, SIGNAL( textChanged( const QString & ) ), SLOT( slotWidgetChanged() ) );
+ connect( m_widget->m_createdOn, SIGNAL( textChanged( const QString & ) ), SLOT( slotWidgetChanged() ) );
+ connect( m_widget->m_creator, SIGNAL( textChanged( const QString & ) ), SLOT( slotWidgetChanged() ) );
+ connect( m_widget->m_description, SIGNAL( textChanged( const QString & ) ), SLOT( slotWidgetChanged() ) );
+ connect( m_widget->m_disclaimer, SIGNAL( textChanged( const QString & ) ), SLOT( slotWidgetChanged() ) );
+ connect( m_widget->m_query, SIGNAL( textChanged( const QString & ) ), SLOT( slotWidgetChanged() ) );
+ connect( m_widget->m_archive, SIGNAL( textChanged( const QString & ) ), SLOT( slotWidgetChanged() ) );
+ connect( m_widget->m_maxUsers, SIGNAL( textChanged( const QString & ) ), SLOT( slotWidgetChanged() ) );
+ connect( m_widget->m_btnAddAcl, SIGNAL( clicked() ), SLOT( slotWidgetChanged() ) );
+ connect( m_widget->m_btnEditAcl, SIGNAL( clicked() ), SLOT( slotWidgetChanged() ) );
+ connect( m_widget->m_btnDeleteAcl, SIGNAL( clicked() ), SLOT( slotWidgetChanged() ) );
+ setMainWidget( m_widget );
+ show();
+}
+
+GroupWise::Chatroom GroupWiseChatPropsDialog::room()
+{
+ GroupWise::Chatroom room;
+ room.description = m_widget->m_description->text();
+ room.displayName = m_widget->m_displayName->text();
+ room.disclaimer = m_widget->m_disclaimer->text();
+ room.ownerDN = m_widget->m_owner->text();
+ room.query = m_widget->m_query->text();
+ room.topic = m_widget->m_topic->text();
+ room.archive = m_widget->m_archive->isChecked();
+ room.maxUsers = m_widget->m_maxUsers->text().toInt();
+
+// room.
+ return room;
+}
+
+void GroupWiseChatPropsDialog::slotWidgetChanged()
+{
+ m_dirty = true;
+}
+
+#include "gwchatpropsdialog.moc"
diff --git a/kopete/protocols/groupwise/ui/gwchatpropsdialog.h b/kopete/protocols/groupwise/ui/gwchatpropsdialog.h
new file mode 100644
index 00000000..058d6b20
--- /dev/null
+++ b/kopete/protocols/groupwise/ui/gwchatpropsdialog.h
@@ -0,0 +1,69 @@
+/*
+ Kopete Groupwise Protocol
+ gwchatpropsdialog.h - dialog for viewing/modifying chat properties
+
+ Copyright (c) 2005 SUSE Linux AG http://www.suse.com
+
+ Kopete (c) 2002-2005 by the Kopete developers <[email protected]>
+
+ *************************************************************************
+ * *
+ * This library 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 GWCHATPROPSDIALOG_H
+#define GWCHATPROPSDIALOG_H
+
+#include <kdialogbase.h>
+
+#include "gwchatrooms.h"
+
+class GroupWiseChatPropsWidget;
+/**
+ * Dialog for viewing/modifying chat properties.
+ * Chatroom list dialog gets props from manager
+ * Chatroom list dialog opens chatpropsdlg using props, connects to OkClicked signal
+ * User makes changes
+ * CLD asks CPD for changes.
+ * CLD passes changes to manager
+ * manager sends ChatUpdate to server
+ * on success, manager updates own model.
+
+ 1) Create dialog with populated widget from supplied Chatroom.
+ 2) Add readonly mode.
+ 3) Track which things changed? Easier to get the changed Chatroom back and diff in the manager, simpler api connecting
+ */
+class GroupWiseChatPropsDialog : public KDialogBase
+{
+ Q_OBJECT
+ public:
+ /**
+ * Construct an empty dialog
+ */
+ GroupWiseChatPropsDialog( QWidget * parent, const char * name );
+ /**
+ * Construct a populated dialog
+ */
+ GroupWiseChatPropsDialog( const GroupWise::Chatroom & room, bool readOnly,
+ QWidget * parent, const char * name );
+
+ ~GroupWiseChatPropsDialog() {}
+
+ bool dirty() { return m_dirty; };
+ GroupWise::Chatroom room();
+ protected:
+ void initialise();
+ protected slots:
+ void slotWidgetChanged();
+ private:
+ GroupWiseChatPropsWidget * m_widget;
+ GroupWise::Chatroom m_room;
+ bool m_dirty;
+};
+
+#endif
diff --git a/kopete/protocols/groupwise/ui/gwchatpropswidget.ui b/kopete/protocols/groupwise/ui/gwchatpropswidget.ui
new file mode 100644
index 00000000..ecb764b9
--- /dev/null
+++ b/kopete/protocols/groupwise/ui/gwchatpropswidget.ui
@@ -0,0 +1,394 @@
+<!DOCTYPE UI><UI version="3.3" stdsetdef="1">
+<class>GroupWiseChatPropsWidget</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>GroupWiseChatPropsWidget</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>493</width>
+ <height>425</height>
+ </rect>
+ </property>
+ <property name="caption">
+ <string>GroupWiseChatPropertiesWidget</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>m_displayName</cstring>
+ </property>
+ <property name="text">
+ <string>DISPLAY NAME</string>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout16</cstring>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLineEdit" row="4" column="1">
+ <property name="name">
+ <cstring>m_creator</cstring>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ <property name="readOnly">
+ <bool>true</bool>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>The user who created the chatroom</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="2" column="2">
+ <property name="name">
+ <cstring>textLabel10_2</cstring>
+ </property>
+ <property name="text">
+ <string>Query:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>m_firstName_2</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>lblTopic</cstring>
+ </property>
+ <property name="text">
+ <string>Topic:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>m_displayName</cstring>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="1" column="3">
+ <property name="name">
+ <cstring>m_disclaimer</cstring>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ <property name="readOnly">
+ <bool>false</bool>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>A disclaimer for users entering the chatroom</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="2" column="0">
+ <property name="name">
+ <cstring>m__2_2</cstring>
+ </property>
+ <property name="text">
+ <string>Owner:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>m_displayName_3</cstring>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="1" column="1">
+ <property name="name">
+ <cstring>m_topic</cstring>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ <property name="readOnly">
+ <bool>false</bool>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>The current topic of the discussion</string>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="2" column="3">
+ <property name="name">
+ <cstring>m_query</cstring>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ <property name="readOnly">
+ <bool>true</bool>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>UNKNOWN</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="3" column="2">
+ <property name="name">
+ <cstring>textLabel11_2_2</cstring>
+ </property>
+ <property name="text">
+ <string>Maximum Users:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>m_lastName_2_2</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="3" column="0">
+ <property name="name">
+ <cstring>m__2_2_2</cstring>
+ </property>
+ <property name="text">
+ <string>Created on:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>m_displayName_3</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="2">
+ <property name="name">
+ <cstring>lbl_displayName_2</cstring>
+ </property>
+ <property name="text">
+ <string>Disclaimer:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>m_displayName_2</cstring>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="5" column="1">
+ <property name="name">
+ <cstring>m_description</cstring>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ <property name="readOnly">
+ <bool>true</bool>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>General description of the chatroom</string>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="3" column="3">
+ <property name="name">
+ <cstring>m_maxUsers</cstring>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ <property name="readOnly">
+ <bool>true</bool>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Maximum simultaneous users allowed in the chatroom</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="4" column="0">
+ <property name="name">
+ <cstring>textLabel10</cstring>
+ </property>
+ <property name="text">
+ <string>Creator:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>m_firstName</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="5" column="0">
+ <property name="name">
+ <cstring>textLabel11</cstring>
+ </property>
+ <property name="text">
+ <string>Description:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>m_lastName</cstring>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="3" column="1">
+ <property name="name">
+ <cstring>m_createdOn</cstring>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ <property name="readOnly">
+ <bool>false</bool>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Date and time the chatroom was created</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="4" column="2">
+ <property name="name">
+ <cstring>m_archive</cstring>
+ </property>
+ <property name="text">
+ <string>Archived</string>
+ </property>
+ <property name="accel">
+ <string></string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Indicates if the chatroom is being archived on the server</string>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="2" column="1">
+ <property name="name">
+ <cstring>m_owner</cstring>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ <property name="readOnly">
+ <bool>false</bool>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>The user who owns this chatroom</string>
+ </property>
+ </widget>
+ <widget class="Line" row="0" column="0" rowspan="1" colspan="4">
+ <property name="name">
+ <cstring>line4</cstring>
+ </property>
+ <property name="frameShape">
+ <enum>HLine</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>Sunken</enum>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QButtonGroup">
+ <property name="name">
+ <cstring>buttonGroup2</cstring>
+ </property>
+ <property name="title">
+ <string>Default Access</string>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>m_chkRead</cstring>
+ </property>
+ <property name="text">
+ <string>Read Message</string>
+ </property>
+ <property name="accel">
+ <string></string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>General permission to read messages in the chatroom</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>m_chkWrite</cstring>
+ </property>
+ <property name="text">
+ <string>Write Message</string>
+ </property>
+ <property name="accel">
+ <string></string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>General permission to write messages in the chatroom</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>m_chkModify</cstring>
+ </property>
+ <property name="text">
+ <string>Modify Access</string>
+ </property>
+ <property name="accel">
+ <string></string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>General permission to modify the chatroom's access control list</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel1</cstring>
+ </property>
+ <property name="text">
+ <string>Access Control List</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>kListBox1</cstring>
+ </property>
+ </widget>
+ <widget class="KListBox">
+ <property name="name">
+ <cstring>m_acl</cstring>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Access permissions for specific users</string>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout15</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="KPushButton">
+ <property name="name">
+ <cstring>m_btnAddAcl</cstring>
+ </property>
+ <property name="text">
+ <string>A&amp;dd</string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Add a new ACL entry</string>
+ </property>
+ </widget>
+ <widget class="KPushButton">
+ <property name="name">
+ <cstring>m_btnEditAcl</cstring>
+ </property>
+ <property name="text">
+ <string>Ed&amp;it</string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Edit an existing ACL entry</string>
+ </property>
+ </widget>
+ <widget class="KPushButton">
+ <property name="name">
+ <cstring>m_btnDeleteAcl</cstring>
+ </property>
+ <property name="text">
+ <string>D&amp;elete</string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Delete a ACL entry</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ </vbox>
+</widget>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+ <includehint>klistbox.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+</includehints>
+</UI>
diff --git a/kopete/protocols/groupwise/ui/gwchatsearchdialog.cpp b/kopete/protocols/groupwise/ui/gwchatsearchdialog.cpp
new file mode 100644
index 00000000..fb67a03e
--- /dev/null
+++ b/kopete/protocols/groupwise/ui/gwchatsearchdialog.cpp
@@ -0,0 +1,106 @@
+/*
+ Kopete Groupwise Protocol
+ gwchatsearchdialog.cpp - dialog for searching for chatrooms
+
+ Copyright (c) 2005 SUSE Linux AG http://www.suse.com
+
+ Kopete (c) 2002-2005 by the Kopete developers <[email protected]>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include <qmap.h>
+
+#include <klistview.h>
+#include <klistviewsearchline.h>
+
+#include <kpushbutton.h>
+#include <kdebug.h>
+#include <klocale.h>
+#include "client.h"
+#include "chatroommanager.h"
+
+#include "gwaccount.h"
+#include "gwprotocol.h"
+#include "gwchatsearchwidget.h"
+#include "gwchatpropsdialog.h"
+
+#include "gwchatsearchdialog.h"
+
+GroupWiseChatSearchDialog::GroupWiseChatSearchDialog( GroupWiseAccount * account, QWidget *parent, const char *name )
+ : KDialogBase( parent, name, false, i18n( "Search Chatrooms" ),
+ KDialogBase::Ok|KDialogBase::Apply|KDialogBase::Cancel, Ok, true ), m_account( account )
+{
+ m_widget = new GroupWiseChatSearchWidget( this );
+// m_widget->m_searchLineWidget->createSearchLine( m_widget->m_chatrooms );
+ setMainWidget( m_widget );
+
+ m_manager = m_account->client()->chatroomManager();
+
+ connect ( m_manager, SIGNAL( updated() ), SLOT( slotManagerUpdated() ) );
+ connect ( m_manager, SIGNAL( gotProperties( const GroupWise::Chatroom & ) ),
+ SLOT( slotGotProperties( const GroupWise::Chatroom & ) ) );
+
+ connect( m_widget->m_btnRefresh, SIGNAL( clicked() ), SLOT( slotUpdateClicked() ) );
+ connect( m_widget->m_btnProperties, SIGNAL( clicked() ), SLOT( slotPropertiesClicked() ) );
+
+ m_manager->updateRooms();
+ show();
+}
+
+GroupWiseChatSearchDialog::~GroupWiseChatSearchDialog()
+{
+}
+
+void GroupWiseChatSearchDialog::slotUpdateClicked()
+{
+ kdDebug ( GROUPWISE_DEBUG_GLOBAL ) << "updating chatroom list " << endl;
+ m_widget->m_chatrooms->clear();
+ QListViewItem * first = m_widget->m_chatrooms->firstChild();
+ QString updateMessage = i18n("Updating chatroom list..." );
+/* if ( first )
+ new QListViewItem( first, updateMessage );
+ else*/
+ new QListViewItem( m_widget->m_chatrooms, updateMessage );
+ m_manager->updateRooms();
+
+}
+
+void GroupWiseChatSearchDialog::slotManagerUpdated()
+{
+ ChatroomMap rooms = m_manager->rooms();
+ ChatroomMap::iterator it = rooms.begin();
+ const ChatroomMap::iterator end = rooms.end();
+ while ( it != end )
+ {
+ new QListViewItem( m_widget->m_chatrooms,
+ it.data().displayName,
+ m_account->protocol()->dnToDotted( it.data().ownerDN ),
+ QString::number( it.data().participantsCount ) );
+ ++it;
+ }
+}
+
+void GroupWiseChatSearchDialog::slotPropertiesClicked()
+{
+ QListViewItem * selected = m_widget->m_chatrooms->selectedItem();
+ if ( selected )
+ {
+ m_manager->requestProperties( selected->text( 0 ) );
+ }
+}
+
+void GroupWiseChatSearchDialog::slotGotProperties(const GroupWise::Chatroom & room)
+{
+ kdDebug( GROUPWISE_DEBUG_GLOBAL ) << k_funcinfo << endl;
+ new GroupWiseChatPropsDialog( room, true, this, "chatpropsdlg" );
+}
+
+#include "gwchatsearchdialog.moc"
diff --git a/kopete/protocols/groupwise/ui/gwchatsearchdialog.h b/kopete/protocols/groupwise/ui/gwchatsearchdialog.h
new file mode 100644
index 00000000..667e6394
--- /dev/null
+++ b/kopete/protocols/groupwise/ui/gwchatsearchdialog.h
@@ -0,0 +1,49 @@
+/*
+ Kopete Groupwise Protocol
+ gwchatsearchdialog.h - dialog for searching for chatrooms
+
+ Copyright (c) 2005 SUSE Linux AG http://www.suse.com
+
+ Kopete (c) 2002-2005 by the Kopete developers <[email protected]>
+
+ *************************************************************************
+ * *
+ * This library 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 GWCHATSEARCHDIALOG_H
+#define GWCHATSEARCHDIALOG_H
+
+class GroupWiseAccount;
+class GroupWiseChatSearchWidget;
+
+#include "gwchatrooms.h"
+
+#include <kdialogbase.h>
+
+class GroupWiseChatSearchDialog : public KDialogBase
+{
+ Q_OBJECT
+ public:
+ GroupWiseChatSearchDialog( GroupWiseAccount * account, QWidget * parent, const char * name );
+ ~GroupWiseChatSearchDialog();
+ protected:
+ void populateWidget();
+ protected slots:
+ /* Button handlers */
+ void slotPropertiesClicked();
+ void slotUpdateClicked();
+ /* Manager update handler */
+ void slotManagerUpdated();
+ void slotGotProperties( const GroupWise::Chatroom & room );
+ private:
+ GroupWiseAccount * m_account;
+ ChatroomManager * m_manager;
+ GroupWiseChatSearchWidget * m_widget;
+};
+#endif
diff --git a/kopete/protocols/groupwise/ui/gwchatsearchwidget.ui b/kopete/protocols/groupwise/ui/gwchatsearchwidget.ui
new file mode 100644
index 00000000..f6f2014c
--- /dev/null
+++ b/kopete/protocols/groupwise/ui/gwchatsearchwidget.ui
@@ -0,0 +1,116 @@
+<!DOCTYPE UI><UI version="3.3" stdsetdef="1">
+<class>GroupWiseChatSearchWidget</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>GroupWiseChatSearchWidget</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>579</width>
+ <height>480</height>
+ </rect>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="KListView">
+ <column>
+ <property name="text">
+ <string>Chatroom </string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <column>
+ <property name="text">
+ <string>Owner</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <column>
+ <property name="text">
+ <string>Members</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <property name="name">
+ <cstring>m_chatrooms</cstring>
+ </property>
+ <property name="allColumnsShowFocus">
+ <bool>true</bool>
+ </property>
+ <property name="fullWidth">
+ <bool>true</bool>
+ </property>
+ <property name="itemsMovable">
+ <bool>false</bool>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout2</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="KPushButton">
+ <property name="name">
+ <cstring>m_btnProperties</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Properties</string>
+ </property>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer3</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>340</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>m_btnRefresh</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Refresh</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ </vbox>
+</widget>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+ <includehint>klistview.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+</includehints>
+</UI>
diff --git a/kopete/protocols/groupwise/ui/gwcontactproperties.cpp b/kopete/protocols/groupwise/ui/gwcontactproperties.cpp
new file mode 100644
index 00000000..120296ce
--- /dev/null
+++ b/kopete/protocols/groupwise/ui/gwcontactproperties.cpp
@@ -0,0 +1,144 @@
+/*
+ Kopete Groupwise Protocol
+ gwcontactproperties.cpp - dialog showing a contact's server side properties
+
+ Copyright (c) 2004 SUSE Linux AG http://www.suse.com
+
+ Kopete (c) 2002-2004 by the Kopete developers <[email protected]>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include <qclipboard.h>
+#include <qheader.h>
+#include <qlabel.h>
+#include <qlineedit.h>
+#include <klistview.h>
+#include <qmap.h>
+#include <qpopupmenu.h>
+
+#include <kapplication.h>
+#include <kdebug.h>
+#include <kdialogbase.h>
+#include <klocale.h>
+#include <kopeteglobal.h>
+#include <kopeteonlinestatus.h>
+#include <kopetemetacontact.h>
+#include <kopeteuiglobal.h>
+#include <kaction.h>
+#include <kstdaction.h>
+
+#include "gwcontact.h"
+#include "gwcontactpropswidget.h"
+#include "gwprotocol.h"
+
+#include "gwcontactproperties.h"
+
+GroupWiseContactProperties::GroupWiseContactProperties( GroupWiseContact * contact, QWidget *parent, const char *name)
+ : QObject(parent, name)
+{
+ init();
+ // set up the contents of the props widget
+ m_propsWidget->m_userId->setText( contact->contactId() );
+ m_propsWidget->m_status->setText( contact->onlineStatus().description() );
+ m_propsWidget->m_displayName->setText( contact->metaContact()->displayName() );
+ m_propsWidget->m_firstName->setText( contact->property( Kopete::Global::Properties::self()->firstName() ).value().toString() );
+ m_propsWidget->m_lastName->setText( contact->property( Kopete::Global::Properties::self()->lastName() ).value().toString() );
+
+ setupProperties( contact->serverProperties() );
+ m_dialog->show();
+}
+
+GroupWiseContactProperties::GroupWiseContactProperties( GroupWise::ContactDetails cd, QWidget *parent, const char *name )
+ : QObject(parent, name)
+{
+ init();
+ // set up the contents of the props widget
+ m_propsWidget->m_userId->setText( GroupWiseProtocol::protocol()->dnToDotted( cd.dn ) );
+ m_propsWidget->m_status->setText( GroupWiseProtocol::protocol()->gwStatusToKOS( cd.status ).description() );
+ m_propsWidget->m_displayName->setText( cd.fullName.isEmpty() ? ( cd.givenName + " " + cd.surname ) : cd.fullName );
+ m_propsWidget->m_firstName->setText( cd.givenName );
+ m_propsWidget->m_lastName->setText( cd.surname );
+
+ setupProperties( cd.properties );
+
+ m_dialog->show();
+}
+
+GroupWiseContactProperties::~GroupWiseContactProperties()
+{
+}
+
+void GroupWiseContactProperties::init()
+{
+ m_dialog = new KDialogBase( ::qt_cast<QWidget*>( parent() ), "gwcontactpropsdialog", false, i18n( "Contact Properties" ), KDialogBase::Ok );
+ m_propsWidget = new GroupWiseContactPropsWidget( m_dialog );
+ // set up the context menu and copy action
+ m_copyAction = KStdAction::copy( this, SLOT( slotCopy() ), 0 );
+ connect( m_propsWidget->m_propsView,
+ SIGNAL( contextMenuRequested( QListViewItem *, const QPoint & , int) ),
+ SLOT( slotShowContextMenu( QListViewItem *, const QPoint & ) ) );
+
+ // insert the props widget into the dialog
+ m_dialog->setMainWidget( m_propsWidget );
+}
+
+void GroupWiseContactProperties::setupProperties( QMap< QString, QString > serverProps )
+{
+ m_propsWidget->m_propsView->header()->hide();
+ QMap< QString, QString >::Iterator it;
+ QMap< QString, QString >::Iterator end = serverProps.end();
+ for ( it = serverProps.begin(); it != end; ++it )
+ {
+ kdDebug( GROUPWISE_DEBUG_GLOBAL ) << " adding property: " << it.key() << ", " << it.data() << endl;
+ QString key = it.key();
+ QString localised;
+ if ( key == "telephoneNumber" )
+ localised = i18n( "Telephone Number" );
+ else if ( key == "OU" )
+ localised = i18n( "Department" );
+ else if ( key == "L" )
+ localised = i18n( "Location" );
+ else if ( key == "mailstop" )
+ localised = i18n( "Mailstop" );
+ else if ( key == "personalTitle" )
+ localised = i18n( "Personal Title" );
+ else if ( key == "title" )
+ localised = i18n( "Title" );
+ else if ( key == "Internet EMail Address" )
+ localised = i18n( "Email Address" );
+ else
+ localised = key;
+
+ new KListViewItem( m_propsWidget->m_propsView, localised, it.data() );
+ }
+}
+
+void GroupWiseContactProperties::slotShowContextMenu( QListViewItem * item, const QPoint & pos )
+{
+ if ( item )
+ kdDebug( GROUPWISE_DEBUG_GLOBAL ) << "for item " << item->text(0) << ", " << item->text(1) << endl;
+ else
+ kdDebug( GROUPWISE_DEBUG_GLOBAL ) << "no selected item" << endl;
+ QPopupMenu * popupMenu = new QPopupMenu( m_propsWidget->m_propsView );
+ m_copyAction->plug( popupMenu );
+ popupMenu->exec( pos );
+}
+
+void GroupWiseContactProperties::slotCopy()
+{
+ kdDebug( GROUPWISE_DEBUG_GLOBAL ) << k_funcinfo << endl;
+ if ( m_propsWidget->m_propsView->currentItem() )
+ {
+ QClipboard *cb = kapp->clipboard();
+ cb->setText( m_propsWidget->m_propsView->currentItem()->text( 1 ) );
+ }
+}
+#include "gwcontactproperties.moc"
diff --git a/kopete/protocols/groupwise/ui/gwcontactproperties.h b/kopete/protocols/groupwise/ui/gwcontactproperties.h
new file mode 100644
index 00000000..5684cf2a
--- /dev/null
+++ b/kopete/protocols/groupwise/ui/gwcontactproperties.h
@@ -0,0 +1,60 @@
+/*
+ Kopete Groupwise Protocol
+ gwcontactproperties.h - dialog showing a contact's server side properties
+
+ Copyright (c) 2004 SUSE Linux AG http://www.suse.com
+
+ Kopete (c) 2002-2004 by the Kopete developers <[email protected]>
+
+ *************************************************************************
+ * *
+ * This library 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 GROUPWISECONTACTPROPERTIES_H
+#define GROUPWISECONTACTPROPERTIES_H
+
+
+#include <qobject.h>
+
+class GroupWiseContactPropsWidget;
+class KDialogBase;
+class QListViewItem;
+class KAction;
+
+/**
+Logic, wrapping UI, for displaying contact properties
+
+@author SUSE AG
+*/
+class GroupWiseContactProperties : public QObject
+{
+Q_OBJECT
+public:
+ /**
+ * Display properties given a GroupWiseContact
+ */
+ GroupWiseContactProperties( GroupWiseContact * contact, QWidget *parent, const char *name );
+ /**
+ * Display properties given a GroupWise::ContactDetails
+ */
+ GroupWiseContactProperties( GroupWise::ContactDetails contactDetails, QWidget *parent = 0, const char *name = 0 );
+ ~GroupWiseContactProperties();
+protected:
+ void setupProperties( QMap< QString, QString > serverProps );
+ void init();
+protected slots:
+ void slotShowContextMenu( QListViewItem *, const QPoint & );
+ void slotCopy();
+private:
+ GroupWiseContactPropsWidget * m_propsWidget;
+ KAction * m_copyAction;
+ KDialogBase * m_dialog;
+};
+
+#endif
diff --git a/kopete/protocols/groupwise/ui/gwcontactpropswidget.ui b/kopete/protocols/groupwise/ui/gwcontactpropswidget.ui
new file mode 100644
index 00000000..3aece991
--- /dev/null
+++ b/kopete/protocols/groupwise/ui/gwcontactpropswidget.ui
@@ -0,0 +1,211 @@
+<!DOCTYPE UI><UI version="3.3" stdsetdef="1">
+<class>GroupWiseContactPropsWidget</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>GroupWiseContactPropsWidget</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>373</width>
+ <height>444</height>
+ </rect>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>m_userId</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>5</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>3</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>USER_ID</string>
+ </property>
+ </widget>
+ <widget class="Line">
+ <property name="name">
+ <cstring>line4</cstring>
+ </property>
+ <property name="frameShape">
+ <enum>HLine</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>Sunken</enum>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout15</cstring>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLineEdit" row="4" column="1">
+ <property name="name">
+ <cstring>m_lastName</cstring>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ <property name="readOnly">
+ <bool>true</bool>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Change the display name used for this contact</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>textLabel14</cstring>
+ </property>
+ <property name="text">
+ <string>Status:</string>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="2" column="1">
+ <property name="name">
+ <cstring>m_displayName</cstring>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ <property name="readOnly">
+ <bool>false</bool>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Change the display name used for this contact</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="1" rowspan="2" colspan="1">
+ <property name="name">
+ <cstring>m_status</cstring>
+ </property>
+ <property name="text">
+ <string>USER_STATUS</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="3" column="0">
+ <property name="name">
+ <cstring>textLabel10</cstring>
+ </property>
+ <property name="text">
+ <string>First name:</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="0" rowspan="2" colspan="1">
+ <property name="name">
+ <cstring>lbl_displayName</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Display name:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>m_displayName</cstring>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="3" column="1">
+ <property name="name">
+ <cstring>m_firstName</cstring>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ <property name="readOnly">
+ <bool>true</bool>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Change the display name used for this contact</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="4" column="0">
+ <property name="name">
+ <cstring>textLabel11</cstring>
+ </property>
+ <property name="text">
+ <string>Last name:</string>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="Line">
+ <property name="name">
+ <cstring>line1_2</cstring>
+ </property>
+ <property name="frameShape">
+ <enum>HLine</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>Sunken</enum>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel15</cstring>
+ </property>
+ <property name="text">
+ <string>Additional properties:</string>
+ </property>
+ </widget>
+ <widget class="KListView">
+ <column>
+ <property name="text">
+ <string>Property</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <column>
+ <property name="text">
+ <string>Value</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <property name="name">
+ <cstring>m_propsView</cstring>
+ </property>
+ <property name="allColumnsShowFocus">
+ <bool>true</bool>
+ </property>
+ <property name="resizeMode">
+ <enum>AllColumns</enum>
+ </property>
+ <property name="fullWidth">
+ <bool>true</bool>
+ </property>
+ <property name="itemsMovable">
+ <bool>false</bool>
+ </property>
+ </widget>
+ </vbox>
+</widget>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+ <includehint>klistview.h</includehint>
+</includehints>
+</UI>
diff --git a/kopete/protocols/groupwise/ui/gwcontactsearch.ui b/kopete/protocols/groupwise/ui/gwcontactsearch.ui
new file mode 100644
index 00000000..868072ce
--- /dev/null
+++ b/kopete/protocols/groupwise/ui/gwcontactsearch.ui
@@ -0,0 +1,386 @@
+<!DOCTYPE UI><UI version="3.3" stdsetdef="1">
+<class>GroupWiseContactSearchWidget</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>GroupWiseContactSearchWidget</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>435</width>
+ <height>410</height>
+ </rect>
+ </property>
+ <property name="caption">
+ <string>Search GroupWise Messenger</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout13</cstring>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>textLabel1</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;First name</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>m_firstName</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="2" column="0">
+ <property name="name">
+ <cstring>textLabel3</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;User ID</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>m_userId</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="3" column="0">
+ <property name="name">
+ <cstring>textLabel4</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Title</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>m_title</cstring>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="2" column="2">
+ <property name="name">
+ <cstring>m_userId</cstring>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="0" column="2">
+ <property name="name">
+ <cstring>m_firstName</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="4" column="0">
+ <property name="name">
+ <cstring>textLabel5</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Department</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>m_dept</cstring>
+ </property>
+ </widget>
+ <widget class="QComboBox" row="2" column="1">
+ <item>
+ <property name="text">
+ <string>contains</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>begins with</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>equals</string>
+ </property>
+ </item>
+ <property name="name">
+ <cstring>m_userIdOperation</cstring>
+ </property>
+ </widget>
+ <widget class="QComboBox" row="0" column="1">
+ <item>
+ <property name="text">
+ <string>contains</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>begins with</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>equals</string>
+ </property>
+ </item>
+ <property name="name">
+ <cstring>m_firstNameOperation</cstring>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="4" column="2">
+ <property name="name">
+ <cstring>m_dept</cstring>
+ </property>
+ </widget>
+ <widget class="QComboBox" row="1" column="1">
+ <item>
+ <property name="text">
+ <string>contains</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>begins with</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>equals</string>
+ </property>
+ </item>
+ <property name="name">
+ <cstring>m_lastNameOperation</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>textLabel2</cstring>
+ </property>
+ <property name="text">
+ <string>Last &amp;name</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>m_lastName</cstring>
+ </property>
+ </widget>
+ <widget class="QPushButton" row="1" column="3">
+ <property name="name">
+ <cstring>m_clear</cstring>
+ </property>
+ <property name="text">
+ <string>Cl&amp;ear</string>
+ </property>
+ </widget>
+ <widget class="QComboBox" row="4" column="1">
+ <item>
+ <property name="text">
+ <string>contains</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>begins with</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>equals</string>
+ </property>
+ </item>
+ <property name="name">
+ <cstring>m_deptOperation</cstring>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="3" column="2">
+ <property name="name">
+ <cstring>m_title</cstring>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="1" column="2">
+ <property name="name">
+ <cstring>m_lastName</cstring>
+ </property>
+ </widget>
+ <widget class="QPushButton" row="0" column="3">
+ <property name="name">
+ <cstring>m_search</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Search</string>
+ </property>
+ <property name="default">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="QComboBox" row="3" column="1">
+ <item>
+ <property name="text">
+ <string>contains</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>begins with</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>equals</string>
+ </property>
+ </item>
+ <property name="name">
+ <cstring>m_titleOperation</cstring>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="Line">
+ <property name="name">
+ <cstring>line1</cstring>
+ </property>
+ <property name="frameShape">
+ <enum>HLine</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>Sunken</enum>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel9</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Results:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>m_results</cstring>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout12</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QListView">
+ <column>
+ <property name="text">
+ <string>Status</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <column>
+ <property name="text">
+ <string>First Name</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <column>
+ <property name="text">
+ <string>Last Name</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <column>
+ <property name="text">
+ <string>User ID</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <property name="name">
+ <cstring>m_results</cstring>
+ </property>
+ <property name="resizePolicy">
+ <enum>AutoOneFit</enum>
+ </property>
+ <property name="resizeMode">
+ <enum>AllColumns</enum>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout8</cstring>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>m_details</cstring>
+ </property>
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="text">
+ <string>Detai&amp;ls</string>
+ </property>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer6</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>141</height>
+ </size>
+ </property>
+ </spacer>
+ </vbox>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>m_matchCount</cstring>
+ </property>
+ <property name="text">
+ <string>0 matching users found</string>
+ </property>
+ </widget>
+ </vbox>
+</widget>
+<tabstops>
+ <tabstop>m_firstName</tabstop>
+ <tabstop>m_lastNameOperation</tabstop>
+ <tabstop>m_lastName</tabstop>
+ <tabstop>m_userIdOperation</tabstop>
+ <tabstop>m_userId</tabstop>
+ <tabstop>m_titleOperation</tabstop>
+ <tabstop>m_title</tabstop>
+ <tabstop>m_deptOperation</tabstop>
+ <tabstop>m_dept</tabstop>
+ <tabstop>m_search</tabstop>
+ <tabstop>m_clear</tabstop>
+ <tabstop>m_results</tabstop>
+ <tabstop>m_details</tabstop>
+ <tabstop>m_firstNameOperation</tabstop>
+</tabstops>
+<layoutdefaults spacing="6" margin="11"/>
+</UI>
diff --git a/kopete/protocols/groupwise/ui/gwcustomstatusedit.ui b/kopete/protocols/groupwise/ui/gwcustomstatusedit.ui
new file mode 100644
index 00000000..43a9af15
--- /dev/null
+++ b/kopete/protocols/groupwise/ui/gwcustomstatusedit.ui
@@ -0,0 +1,92 @@
+<!DOCTYPE UI><UI version="3.3" stdsetdef="1">
+<class>GroupWiseCustomStatusEdit</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>GroupWiseCustomStatusEdit</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>260</width>
+ <height>113</height>
+ </rect>
+ </property>
+ <property name="caption">
+ <string></string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout3</cstring>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLineEdit" row="1" column="1">
+ <property name="name">
+ <cstring>m_name</cstring>
+ </property>
+ <property name="frameShape">
+ <enum>LineEditPanel</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>Sunken</enum>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ <widget class="QComboBox" row="0" column="1">
+ <property name="name">
+ <cstring>m_cmbStatus</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>textLabel3</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Status:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>comboBox1</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="2" column="0">
+ <property name="name">
+ <cstring>textLabel2</cstring>
+ </property>
+ <property name="text">
+ <string>Awa&amp;y message:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>lineEdit2</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>textLabel1</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Name:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>lineEdit1</cstring>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="2" column="1">
+ <property name="name">
+ <cstring>m_awayMessage</cstring>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ </vbox>
+</widget>
+<layoutdefaults spacing="6" margin="11"/>
+</UI>
diff --git a/kopete/protocols/groupwise/ui/gwcustomstatuswidget.ui b/kopete/protocols/groupwise/ui/gwcustomstatuswidget.ui
new file mode 100644
index 00000000..8c69aa76
--- /dev/null
+++ b/kopete/protocols/groupwise/ui/gwcustomstatuswidget.ui
@@ -0,0 +1,112 @@
+<!DOCTYPE UI><UI version="3.3" stdsetdef="1">
+<class>GroupWiseCustomStatusWidget</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>GroupWiseCustomStatusWidget</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>343</width>
+ <height>215</height>
+ </rect>
+ </property>
+ <property name="caption">
+ <string></string>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="KListView">
+ <column>
+ <property name="text">
+ <string>Name</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <column>
+ <property name="text">
+ <string>Auto Reply</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <property name="name">
+ <cstring>m_list</cstring>
+ </property>
+ <property name="allColumnsShowFocus">
+ <bool>true</bool>
+ </property>
+ <property name="fullWidth">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout2</cstring>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>m_btnAdd</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Add</string>
+ </property>
+ </widget>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>m_btnEdit</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Edit</string>
+ </property>
+ </widget>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>m_btnRemove</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Remove</string>
+ </property>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer1</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>41</height>
+ </size>
+ </property>
+ </spacer>
+ </vbox>
+ </widget>
+ </hbox>
+</widget>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+ <includehint>klistview.h</includehint>
+</includehints>
+</UI>
diff --git a/kopete/protocols/groupwise/ui/gweditaccountwidget.cpp b/kopete/protocols/groupwise/ui/gweditaccountwidget.cpp
new file mode 100644
index 00000000..87468ccf
--- /dev/null
+++ b/kopete/protocols/groupwise/ui/gweditaccountwidget.cpp
@@ -0,0 +1,136 @@
+/*
+ Kopete GroupWise Protocol
+ gweditaccountwidget.cpp - widget for adding or editing GroupWise accounts
+
+ Copyright (c) 2004 SUSE Linux AG http://www.suse.com
+
+ Based on Testbed
+ Copyright (c) 2003 by Will Stephenson <[email protected]>
+
+ Kopete (c) 2002-2003 by the Kopete developers <[email protected]>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include <qcheckbox.h>
+#include <qlayout.h>
+#include <qlineedit.h>
+#include <qspinbox.h>
+
+#include <kapplication.h>
+#include <kdebug.h>
+#include <kconfig.h>
+#include <klocale.h>
+#include <kmessagebox.h>
+#include <kpassdlg.h>
+#include "kopetepasswordedaccount.h"
+#include "kopetepasswordwidget.h"
+
+#include "gwaccountpreferences.h"
+#include "gwaccount.h"
+#include "gwerror.h"
+#include "gwprotocol.h"
+
+#include "gweditaccountwidget.h"
+
+GroupWiseEditAccountWidget::GroupWiseEditAccountWidget( QWidget* parent, Kopete::Account* theAccount)
+: QWidget( parent ), KopeteEditAccountWidget( theAccount )
+{
+ kdDebug(GROUPWISE_DEBUG_GLOBAL) << k_funcinfo << endl;
+ m_layout = new QVBoxLayout( this );
+ m_preferencesDialog = new GroupWiseAccountPreferences( this );
+ m_layout->addWidget( m_preferencesDialog );
+ connect( m_preferencesDialog->m_password, SIGNAL( changed() ), this, SLOT( configChanged() ) );
+ connect( m_preferencesDialog->m_server, SIGNAL( textChanged( const QString & ) ), this, SLOT( configChanged() ) );
+ connect( m_preferencesDialog->m_port, SIGNAL( valueChanged( int ) ), this, SLOT( configChanged() ) );
+ if ( account() )
+ reOpen();
+ else
+ {
+ // look for a default server and port setting
+ KConfig *config = kapp->config();
+ config->setGroup("GroupWise Messenger");
+ m_preferencesDialog->m_server->setText( config->readEntry( "DefaultServer" ) );
+ m_preferencesDialog->m_port->setValue( config->readNumEntry( "DefaultPort", 8300 ) );
+ }
+ QWidget::setTabOrder( m_preferencesDialog->m_userId, m_preferencesDialog->m_password->mRemembered );
+ QWidget::setTabOrder( m_preferencesDialog->m_password->mRemembered, m_preferencesDialog->m_password->mPassword );
+ QWidget::setTabOrder( m_preferencesDialog->m_password->mPassword, m_preferencesDialog->m_autoConnect );
+
+}
+
+GroupWiseEditAccountWidget::~GroupWiseEditAccountWidget()
+{
+}
+
+GroupWiseAccount *GroupWiseEditAccountWidget::account ()
+{
+ Q_ASSERT( KopeteEditAccountWidget::account() );
+ return dynamic_cast< GroupWiseAccount *>( KopeteEditAccountWidget::account() );
+}
+
+void GroupWiseEditAccountWidget::reOpen()
+{
+ kdDebug(GROUPWISE_DEBUG_GLOBAL) << k_funcinfo << endl;
+
+ m_preferencesDialog->m_password->load( &account()->password () );
+ // Kopete at least <=0.90 doesn't support changing account IDs
+ m_preferencesDialog->m_userId->setDisabled( true );
+ m_preferencesDialog->m_userId->setText( account()->accountId() );
+ m_preferencesDialog->m_password->load( &account()->password() );
+ m_preferencesDialog->m_server->setText( account()->configGroup()->readEntry( "Server") );
+ m_preferencesDialog->m_port->setValue( account()->configGroup()->readNumEntry( "Port" ) );
+ m_preferencesDialog->m_autoConnect->setChecked( account()->excludeConnect() );
+ m_preferencesDialog->m_alwaysAccept->setChecked( account()->configGroup()->readBoolEntry( "AlwaysAcceptInvitations" ) );
+}
+
+Kopete::Account* GroupWiseEditAccountWidget::apply()
+{
+ kdDebug(GROUPWISE_DEBUG_GLOBAL) << k_funcinfo << endl;
+
+ if ( !account() )
+ setAccount( new GroupWiseAccount( GroupWiseProtocol::protocol(), m_preferencesDialog->m_userId->text() ) );
+
+ if(account()->isConnected())
+ {
+ KMessageBox::information(this,
+ i18n("The changes you just made will take effect next time you log in with GroupWise."),
+ i18n("GroupWise Settings Changed While Signed In"));
+ }
+
+ writeConfig();
+
+ return account();
+}
+
+bool GroupWiseEditAccountWidget::validateData()
+{
+ return !( m_preferencesDialog->m_userId->text().isEmpty() || m_preferencesDialog->m_server->text().isEmpty() );
+}
+
+void GroupWiseEditAccountWidget::writeConfig()
+{
+ kdDebug(GROUPWISE_DEBUG_GLOBAL) << k_funcinfo << endl;
+ account()->configGroup()->writeEntry( "Server", m_preferencesDialog->m_server->text() );
+ account()->configGroup()->writeEntry( "Port", QString::number( m_preferencesDialog->m_port->value() ) );
+ account()->configGroup()->writeEntry( "AlwaysAcceptInvitations",
+ m_preferencesDialog->m_alwaysAccept->isChecked() ? "true" : "false" );
+
+ account()->setExcludeConnect( m_preferencesDialog->m_autoConnect->isChecked() );
+ m_preferencesDialog->m_password->save( &account()->password() );
+ settings_changed = false;
+}
+
+void GroupWiseEditAccountWidget::configChanged ()
+{
+ settings_changed = true;
+}
+
+#include "gweditaccountwidget.moc"
diff --git a/kopete/protocols/groupwise/ui/gweditaccountwidget.h b/kopete/protocols/groupwise/ui/gweditaccountwidget.h
new file mode 100644
index 00000000..27c54ee2
--- /dev/null
+++ b/kopete/protocols/groupwise/ui/gweditaccountwidget.h
@@ -0,0 +1,64 @@
+/*
+ Kopete GroupWise Protocol
+ gweditaccountwidget.h - widget for adding or editing GroupWise accounts
+
+ Copyright (c) 2004 SUSE Linux AG http://www.suse.com
+
+ Based on Testbed
+ Copyright (c) 2003 by Will Stephenson <[email protected]>
+
+ Kopete (c) 2002-2003 by the Kopete developers <[email protected]>
+
+ *************************************************************************
+ * *
+ * This library 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 TESTBEDEDITACCOUNTWIDGET_H
+#define TESTBEDEDITACCOUNTWIDGET_H
+
+#include <qwidget.h>
+#include <editaccountwidget.h>
+
+class QVBoxLayout;
+namespace Kopete { class Account; }
+class GroupWiseAccountPreferences;
+
+/**
+ * A widget for editing this protocol's accounts
+ * @author Will Stephenson
+*/
+class GroupWiseEditAccountWidget : public QWidget, public KopeteEditAccountWidget
+{
+Q_OBJECT
+public:
+ GroupWiseEditAccountWidget( QWidget* parent, Kopete::Account* account);
+
+ ~GroupWiseEditAccountWidget();
+
+ /**
+ * Make an account out of the entered data
+ */
+ virtual Kopete::Account* apply();
+ /**
+ * Is the data correct?
+ */
+ virtual bool validateData();
+protected slots:
+ void configChanged();
+protected:
+ bool settings_changed;
+ GroupWiseAccount * account();
+ void reOpen();
+ void writeConfig();
+ Kopete::Account *m_account;
+ QVBoxLayout *m_layout;
+ GroupWiseAccountPreferences *m_preferencesDialog;
+};
+
+#endif
diff --git a/kopete/protocols/groupwise/ui/gwprivacy.ui b/kopete/protocols/groupwise/ui/gwprivacy.ui
new file mode 100644
index 00000000..8b09cab6
--- /dev/null
+++ b/kopete/protocols/groupwise/ui/gwprivacy.ui
@@ -0,0 +1,193 @@
+<!DOCTYPE UI><UI version="3.2" stdsetdef="1">
+<class>GroupWisePrivacyWidget</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>GroupWisePrivacyWidget</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>463</width>
+ <height>314</height>
+ </rect>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel1</cstring>
+ </property>
+ <property name="text">
+ <string>Who can see my online status and send me messages:</string>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout11</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout9</cstring>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel2</cstring>
+ </property>
+ <property name="text">
+ <string>A&amp;llowed</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>m_allowList</cstring>
+ </property>
+ </widget>
+ <widget class="QListBox">
+ <property name="name">
+ <cstring>m_allowList</cstring>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout8</cstring>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <spacer>
+ <property name="name">
+ <cstring>spacer9</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>21</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>m_btnBlock</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Block &gt;&gt;</string>
+ </property>
+ </widget>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>m_btnAllow</cstring>
+ </property>
+ <property name="text">
+ <string>&lt;&lt; Allo&amp;w</string>
+ </property>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer1</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>53</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>m_btnAdd</cstring>
+ </property>
+ <property name="text">
+ <string>A&amp;dd...</string>
+ </property>
+ </widget>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>m_btnRemove</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Remove</string>
+ </property>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer2</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>52</height>
+ </size>
+ </property>
+ </spacer>
+ </vbox>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout10</cstring>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel3</cstring>
+ </property>
+ <property name="text">
+ <string>Bloc&amp;ked</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>m_denyList</cstring>
+ </property>
+ </widget>
+ <widget class="QListBox">
+ <property name="name">
+ <cstring>m_denyList</cstring>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>m_status</cstring>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ </vbox>
+</widget>
+<layoutdefaults spacing="6" margin="11"/>
+</UI>
diff --git a/kopete/protocols/groupwise/ui/gwprivacydialog.cpp b/kopete/protocols/groupwise/ui/gwprivacydialog.cpp
new file mode 100644
index 00000000..d46daf4a
--- /dev/null
+++ b/kopete/protocols/groupwise/ui/gwprivacydialog.cpp
@@ -0,0 +1,349 @@
+/*
+ Kopete Groupwise Protocol
+ gwprivacydialog.cpp - dialog summarising, and editing, the user's privacy settings
+
+ Copyright (c) 2004 SUSE Linux AG http://www.suse.com
+
+ Kopete (c) 2002-2004 by the Kopete developers <[email protected]>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include <qlabel.h>
+#include <qlistbox.h>
+#include <qpushbutton.h>
+#include <qstringlist.h>
+#include <qlistview.h>
+
+#include <kdebug.h>
+#include <kiconloader.h>
+#include <klocale.h>
+#include <kmessagebox.h>
+
+#include "client.h"
+#include "gwaccount.h"
+#include "gwprivacy.h"
+#include "gwprotocol.h"
+#include "gwsearch.h"
+#include "privacymanager.h"
+#include "userdetailsmanager.h"
+#include "gwprivacydialog.h"
+
+class PrivacyLBI : public QListBoxPixmap
+{
+public:
+ PrivacyLBI( QListBox * listBox, const QPixmap & pixmap, const QString & text, const QString & dn )
+ : QListBoxPixmap( listBox, pixmap, text ), m_dn( dn )
+ {
+ }
+ QString dn() { return m_dn; }
+private:
+ QString m_dn;
+};
+
+GroupWisePrivacyDialog::GroupWisePrivacyDialog( GroupWiseAccount * account, QWidget *parent, const char *name )
+ : KDialogBase( parent, name, false, i18n( "Account specific privacy settings", "Manage Privacy for %1" ).arg( account->accountId() ),
+ KDialogBase::Ok|KDialogBase::Apply|KDialogBase::Cancel, Ok, true ), m_account( account ), m_dirty( false ), m_searchDlg(0)
+{
+ m_privacy = new GroupWisePrivacyWidget( this );
+ setMainWidget( m_privacy );
+ PrivacyManager * mgr = m_account->client()->privacyManager();
+ // populate the widget;
+ // admin lock
+ if ( mgr->isPrivacyLocked() )
+ {
+ m_privacy->m_status->setText( i18n( "Privacy settings have been administratively locked" ) );
+ disableWidgets();
+ }
+
+ populateWidgets();
+
+ m_privacy->m_allowList->setSelectionMode( QListBox::Extended );
+ m_privacy->m_denyList->setSelectionMode( QListBox::Extended );
+
+ connect( m_privacy->m_btnAllow, SIGNAL( clicked() ), SLOT( slotAllowClicked() ) );
+ connect( m_privacy->m_btnBlock, SIGNAL( clicked() ), SLOT( slotBlockClicked() ) );
+ connect( m_privacy->m_btnAdd, SIGNAL( clicked() ), SLOT( slotAddClicked() ) );
+ connect( m_privacy->m_btnRemove, SIGNAL( clicked() ), SLOT( slotRemoveClicked() ) );
+ connect( m_privacy->m_allowList, SIGNAL( selectionChanged() ), SLOT( slotAllowListClicked() ) );
+ connect( m_privacy->m_denyList, SIGNAL( selectionChanged() ), SLOT( slotDenyListClicked() ) );
+ connect( mgr, SIGNAL( privacyChanged( const QString &, bool ) ), SLOT( slotPrivacyChanged() ) );
+ m_privacy->m_btnAdd->setEnabled( true );
+ m_privacy->m_btnAllow->setEnabled( false );
+ m_privacy->m_btnBlock->setEnabled( false );
+ m_privacy->m_btnRemove->setEnabled( false );
+
+/* showButtonOK( true );
+ showButtonApply( true );
+ showButtonCancel( true );
+ */
+ show();
+}
+
+GroupWisePrivacyDialog::~GroupWisePrivacyDialog()
+{
+}
+
+void GroupWisePrivacyDialog::populateWidgets()
+{
+ m_dirty = false;
+ PrivacyManager * mgr = m_account->client()->privacyManager();
+
+ // default policy
+ QString defaultPolicyText = i18n( "<Everyone Else>" );
+ if ( mgr->defaultAllow() )
+ m_defaultPolicy = new QListBoxText( m_privacy->m_allowList, defaultPolicyText );
+ else
+ m_defaultPolicy = new QListBoxText( m_privacy->m_denyList, defaultPolicyText );
+
+ QPixmap icon = m_account->protocol()->groupwiseAvailable.iconFor( m_account );
+
+ // allow list
+ QStringList allowList = mgr->allowList();
+ QStringList::Iterator end = allowList.end();
+ for ( QStringList::Iterator it = allowList.begin(); it != end; ++it )
+ {
+ GroupWise::ContactDetails cd = m_account->client()->userDetailsManager()->details( *it );
+ if ( cd.fullName.isEmpty() )
+ cd.fullName = cd.givenName + " " + cd.surname;
+ new PrivacyLBI( m_privacy->m_allowList, icon, cd.fullName, *it );
+ }
+ // deny list
+ QStringList denyList = mgr->denyList();
+ end = denyList.end();
+ for ( QStringList::Iterator it = denyList.begin(); it != end; ++it )
+ {
+ GroupWise::ContactDetails cd = m_account->client()->userDetailsManager()->details( *it );
+ if ( cd.fullName.isEmpty() )
+ cd.fullName = cd.givenName + " " + cd.surname;
+ new PrivacyLBI( m_privacy->m_denyList, icon, cd.fullName, *it );
+ }
+ updateButtonState();
+}
+
+void GroupWisePrivacyDialog::disableWidgets()
+{
+ if ( m_privacy )
+ {
+ m_privacy->m_btnAllow->setEnabled( false );
+ m_privacy->m_btnBlock->setEnabled( false );
+ m_privacy->m_btnAdd->setEnabled( false );
+ m_privacy->m_btnRemove->setEnabled( false );
+ }
+}
+
+void GroupWisePrivacyDialog::slotBlockClicked()
+{
+ // take each selected item from the allow list and add it to the deny list
+ // start at the bottom, as we are changing the contents of the list as we go
+ for( int i = m_privacy->m_allowList->count() - 1; i >= 0 ; --i )
+ {
+ if ( m_privacy->m_allowList->isSelected( i ) )
+ {
+ m_dirty = true;
+ QListBoxItem * lbi = m_privacy->m_allowList->item( i );
+ m_privacy->m_allowList->takeItem( lbi );
+ m_privacy->m_denyList->insertItem( lbi );
+ }
+ }
+ updateButtonState();
+}
+
+void GroupWisePrivacyDialog::slotAllowClicked()
+{
+ // take each selected item from the deny list and add it to the allow list
+ for( int i = m_privacy->m_denyList->count() - 1; i >= 0 ; --i )
+ {
+ if ( m_privacy->m_denyList->isSelected( i ) )
+ {
+ m_dirty = true;
+ QListBoxItem * lbi = m_privacy->m_denyList->item( i );
+ m_privacy->m_denyList->takeItem( lbi );
+ m_privacy->m_allowList->insertItem( lbi );
+ }
+ }
+ updateButtonState();
+}
+
+void GroupWisePrivacyDialog::slotAddClicked()
+{
+ if ( !m_searchDlg )
+ {
+ m_searchDlg = new KDialogBase( this, "privacysearchdialog", false,
+ i18n( "Search for Contact to Block" ),
+ KDialogBase::Ok|KDialogBase::Cancel );
+ m_search = new GroupWiseContactSearch( m_account, QListView::Multi, false, m_searchDlg, "privacysearchwidget" );
+ m_searchDlg->setMainWidget( m_search );
+ connect( m_searchDlg, SIGNAL( okClicked() ), SLOT( slotSearchedForUsers() ) );
+ connect( m_search, SIGNAL( selectionValidates( bool ) ), m_searchDlg, SLOT( enableButtonOK( bool ) ) );
+ m_searchDlg->enableButtonOK( false );
+ }
+ m_searchDlg->show();
+}
+
+void GroupWisePrivacyDialog::slotSearchedForUsers()
+{
+ // create an item for each result, in the block list
+ QValueList< ContactDetails > selected = m_search->selectedResults();
+ QValueList< ContactDetails >::Iterator it = selected.begin();
+ const QValueList< ContactDetails >::Iterator end = selected.end();
+ QPixmap icon = m_account->protocol()->groupwiseAvailable.iconFor( m_account );
+ for ( ; it != end; ++it )
+ {
+ m_dirty = true;
+ m_account->client()->userDetailsManager()->addDetails( *it );
+ if ( (*it).fullName.isEmpty() )
+ (*it).fullName = (*it).givenName + " " + (*it).surname;
+ new PrivacyLBI( m_privacy->m_denyList, icon, (*it).fullName, (*it).dn );
+ }
+}
+
+void GroupWisePrivacyDialog::slotRemoveClicked()
+{
+ // remove any selected items from either list, except the default policy
+ for( int i = m_privacy->m_denyList->count() - 1; i >= 0 ; --i )
+ {
+ if ( m_privacy->m_denyList->isSelected( i ) )
+ {
+ m_dirty = true;
+ QListBoxItem * lbi = m_privacy->m_denyList->item( i );
+ // can't remove the default policy
+ if ( lbi == m_defaultPolicy )
+ continue;
+ m_privacy->m_denyList->removeItem( i );
+ }
+ }
+ for( int i = m_privacy->m_allowList->count() - 1; i >= 0 ; --i )
+ {
+ if ( m_privacy->m_allowList->isSelected( i ) )
+ {
+ m_dirty = true;
+ QListBoxItem * lbi = m_privacy->m_allowList->item( i );
+ // can't remove the default policy
+ if ( lbi == m_defaultPolicy )
+ continue;
+ m_privacy->m_allowList->removeItem( i );
+ }
+ }
+ updateButtonState();
+}
+
+void GroupWisePrivacyDialog::slotAllowListClicked()
+{
+ // don't get into feedback
+ disconnect( m_privacy->m_denyList, SIGNAL( selectionChanged() ), this, SLOT( slotDenyListClicked() ) );
+ m_privacy->m_denyList->clearSelection();
+ connect( m_privacy->m_denyList, SIGNAL( selectionChanged() ), SLOT( slotDenyListClicked() ) );
+ bool selected = false;
+ for( int i = m_privacy->m_allowList->count() - 1; i >= 0 ; --i )
+ {
+ if ( m_privacy->m_allowList->isSelected( i ) )
+ {
+ selected = true;
+ break;
+ }
+ }
+ m_privacy->m_btnAllow->setEnabled( false );
+ m_privacy->m_btnBlock->setEnabled( selected );
+ m_privacy->m_btnRemove->setEnabled( selected );
+}
+
+void GroupWisePrivacyDialog::slotDenyListClicked()
+{
+ // don't get into feedback
+ disconnect( m_privacy->m_allowList, SIGNAL( selectionChanged() ), this, SLOT( slotAllowListClicked() ) );
+ m_privacy->m_allowList->clearSelection();
+ connect( m_privacy->m_allowList, SIGNAL( selectionChanged() ), SLOT( slotAllowListClicked() ) );
+ bool selected = false;
+ for( int i = m_privacy->m_denyList->count() - 1; i >= 0 ; --i )
+ {
+ if ( m_privacy->m_denyList->isSelected( i ) )
+ {
+ selected = true;
+ break;
+ }
+ }
+ m_privacy->m_btnAllow->setEnabled( selected );
+ m_privacy->m_btnBlock->setEnabled( false );
+ m_privacy->m_btnRemove->setEnabled( selected );
+}
+
+void GroupWisePrivacyDialog::slotPrivacyChanged()
+{
+ m_privacy->m_denyList->clear();
+ m_privacy->m_allowList->clear();
+ populateWidgets();
+}
+
+void GroupWisePrivacyDialog::updateButtonState()
+{
+ enableButtonApply( m_dirty );
+}
+
+void GroupWisePrivacyDialog::slotOk()
+{
+ if ( m_dirty )
+ commitChanges();
+ KDialogBase::slotOk();
+}
+
+void GroupWisePrivacyDialog::slotApply()
+{
+ if ( m_dirty )
+ {
+ commitChanges();
+ m_dirty = false;
+ updateButtonState();
+ }
+ KDialogBase::slotApply();
+}
+
+void GroupWisePrivacyDialog::commitChanges()
+{
+ if ( m_account->isConnected() )
+ {
+ bool defaultDeny = false;
+ QStringList denyList;
+ QStringList allowList;
+ // pass on our current allow, deny and default policy to the PrivacyManager
+ for( int i = 0; i < (int)m_privacy->m_denyList->count(); ++i )
+ {
+ if ( m_privacy->m_denyList->item( i ) == m_defaultPolicy )
+ defaultDeny = true;
+ else
+ {
+ PrivacyLBI * lbi = static_cast<PrivacyLBI *>( m_privacy->m_denyList->item( i ) );
+ denyList.append( lbi->dn() );
+ }
+ }
+ for( int i = 0; i < (int)m_privacy->m_allowList->count(); ++i )
+ {
+ if ( m_privacy->m_allowList->item( i ) == m_defaultPolicy )
+ defaultDeny = false;
+ else
+ {
+ PrivacyLBI * lbi = static_cast<PrivacyLBI *>( m_privacy->m_allowList->item( i ) );
+ allowList.append( lbi->dn() );
+ }
+ }
+ PrivacyManager * mgr = m_account->client()->privacyManager();
+ mgr->setPrivacy( defaultDeny, allowList, denyList );
+ }
+ else
+ errorNotConnected();
+}
+
+void GroupWisePrivacyDialog::errorNotConnected()
+{
+ KMessageBox::queuedMessageBox( this, KMessageBox::Information,
+ i18n( "You can only change privacy settings while you are logged in to the GroupWise Messenger server." ) , i18n("'%1' Not Logged In").arg( m_account->accountId() ) );
+}
+
+#include "gwprivacydialog.moc"
diff --git a/kopete/protocols/groupwise/ui/gwprivacydialog.h b/kopete/protocols/groupwise/ui/gwprivacydialog.h
new file mode 100644
index 00000000..a5467e47
--- /dev/null
+++ b/kopete/protocols/groupwise/ui/gwprivacydialog.h
@@ -0,0 +1,67 @@
+/*
+ Kopete Groupwise Protocol
+ gwprivacydialog.h - dialog summarising, and editing, the user's privacy settings
+
+ Copyright (c) 2004 SUSE Linux AG http://www.suse.com
+
+ Kopete (c) 2002-2004 by the Kopete developers <[email protected]>
+
+ *************************************************************************
+ * *
+ * This library 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 GWPRIVACYDIALOG_H
+#define GWPRIVACYDIALOG_H
+
+#include <kdialogbase.h>
+
+class GroupWiseAccount;
+class GroupWisePrivacyWidget;
+class GroupWiseContactSearch;
+class QListBoxItem;
+
+/**
+Logic for the UI part managing the allow and deny lists, and the default privacy setting.
+
+@author Kopete Developers
+*/
+class GroupWisePrivacyDialog : public KDialogBase
+{
+Q_OBJECT
+public:
+ GroupWisePrivacyDialog( GroupWiseAccount * account, QWidget * parent, const char * name );
+ ~GroupWisePrivacyDialog();
+protected:
+ void commitChanges();
+ void errorNotConnected();
+ void disableWidgets();
+ void populateWidgets();
+ void updateButtonState();
+protected slots:
+ void slotAllowClicked();
+ void slotBlockClicked();
+ void slotAddClicked();
+ void slotRemoveClicked();
+ void slotAllowListClicked();
+ void slotDenyListClicked();
+ void slotPrivacyChanged();
+ void slotSearchedForUsers();
+ void slotOk();
+ void slotApply();
+
+private:
+ GroupWiseAccount * m_account;
+ GroupWisePrivacyWidget * m_privacy;
+ GroupWiseContactSearch * m_search;
+ QListBoxItem * m_defaultPolicy;
+ bool m_dirty;
+ KDialogBase * m_searchDlg;
+};
+
+#endif
diff --git a/kopete/protocols/groupwise/ui/gwreceiveinvitationdialog.cpp b/kopete/protocols/groupwise/ui/gwreceiveinvitationdialog.cpp
new file mode 100644
index 00000000..5b0b4014
--- /dev/null
+++ b/kopete/protocols/groupwise/ui/gwreceiveinvitationdialog.cpp
@@ -0,0 +1,77 @@
+/*
+ Kopete Groupwise Protocol
+ gwreceiveinvitationdialog.cpp - dialog shown when the user receives an invitation to chat
+
+ Copyright (c) 2004 SUSE Linux AG http://www.suse.com
+
+ Kopete (c) 2002-2004 by the Kopete developers <[email protected]>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include <qcheckbox.h>
+#include <qlabel.h>
+
+#include <kconfig.h>
+#include <klocale.h>
+#include <kopetecontact.h>
+#include <kopeteglobal.h>
+#include <kopetemetacontact.h>
+#include "client.h"
+#include "gwaccount.h"
+#include "gwcontact.h"
+#include "gwerror.h"
+#include "gwprotocol.h"
+#include "gwshowinvitation.h"
+
+#include "gwreceiveinvitationdialog.h"
+
+ReceiveInvitationDialog::ReceiveInvitationDialog( GroupWiseAccount * account, const ConferenceEvent & event, QWidget *parent, const char *name)
+ : KDialogBase( i18n("Invitation to Conversation"), KDialogBase::Yes|KDialogBase::No, KDialogBase::Yes, KDialogBase::No, parent, name, false )
+{
+ m_account = account;
+ m_guid = event.guid;
+ connect( this, SIGNAL( yesClicked() ), SLOT( slotYesClicked() ) );
+ connect( this, SIGNAL( noClicked() ), SLOT( slotNoClicked() ) );
+
+ GroupWiseContact * c = account->contactForDN( event.user );
+
+ m_wid = new ShowInvitationWidget ( this );
+ if ( c )
+ m_wid->m_contactName->setText( c->metaContact()->displayName() );
+ else //something is very wrong
+ m_wid->m_contactName->setText( event.user );
+
+ m_wid->m_dateTime->setText( KGlobal::locale()->formatDateTime( event.timeStamp ) );
+ m_wid->m_message->setText( QString("<b>%1</b>").arg( event.message ) );
+
+ setMainWidget( m_wid );
+}
+
+ReceiveInvitationDialog::~ReceiveInvitationDialog()
+{
+}
+
+void ReceiveInvitationDialog::slotYesClicked()
+{
+ m_account->client()->joinConference( m_guid );
+ // save the state of always accept invitations
+ QString alwaysAccept = m_wid->cb_dontShowAgain->isChecked() ? "true" : "false";
+ m_account->configGroup()->writeEntry( "AlwaysAcceptInvitations", alwaysAccept );
+ deleteLater();
+}
+
+void ReceiveInvitationDialog::slotNoClicked()
+{
+ m_account->client()->rejectInvitation( m_guid );
+ deleteLater();
+}
+
+#include "gwreceiveinvitationdialog.moc"
diff --git a/kopete/protocols/groupwise/ui/gwreceiveinvitationdialog.h b/kopete/protocols/groupwise/ui/gwreceiveinvitationdialog.h
new file mode 100644
index 00000000..0486c964
--- /dev/null
+++ b/kopete/protocols/groupwise/ui/gwreceiveinvitationdialog.h
@@ -0,0 +1,48 @@
+/*
+ Kopete Groupwise Protocol
+ gwreceiveinvitationdialog.h - dialog shown when the user receives an invitation to chat
+
+ Copyright (c) 2004 SUSE Linux AG http://www.suse.com
+
+ Kopete (c) 2002-2004 by the Kopete developers <[email protected]>
+
+ *************************************************************************
+ * *
+ * This library 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 GWRECEIVEINVITATIONDIALOG_H
+#define GWRECEIVEINVITATIONDIALOG_H
+
+#include <kdialogbase.h>
+
+class ShowInvitationWidget;
+
+/**
+This is the dialog that is shown when you receive an invitation to chat.
+
+@author SUSE AG
+*/
+class ReceiveInvitationDialog : public KDialogBase
+{
+Q_OBJECT
+public:
+ ReceiveInvitationDialog( GroupWiseAccount * account, const ConferenceEvent & event, QWidget *parent, const char *name );
+ ~ReceiveInvitationDialog();
+signals:
+ void invitationAccepted( bool, const GroupWise::ConferenceGuid & guid );
+protected slots:
+ void slotYesClicked();
+ void slotNoClicked();
+private:
+ GroupWiseAccount * m_account;
+ ConferenceGuid m_guid; // the conference we were invited to join.
+ ShowInvitationWidget * m_wid;
+};
+
+#endif
diff --git a/kopete/protocols/groupwise/ui/gwsearch.cpp b/kopete/protocols/groupwise/ui/gwsearch.cpp
new file mode 100644
index 00000000..1c80e3eb
--- /dev/null
+++ b/kopete/protocols/groupwise/ui/gwsearch.cpp
@@ -0,0 +1,281 @@
+/*
+ Kopete Groupwise Protocol
+ gwsearch.cpp - logic for server side search widget
+
+ Copyright (c) 2004 SUSE Linux AG http://www.suse.com
+
+ Kopete (c) 2002-2004 by the Kopete developers <[email protected]>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include <qcombobox.h>
+#include <qlabel.h>
+#include <qlineedit.h>
+#include <qlistview.h>
+#include <qpushbutton.h>
+//#include <qvaluelist.h>
+
+#include <kdebug.h>
+#include <klocale.h>
+
+#include <kopetemetacontact.h>
+
+#include "client.h"
+#include "gwaccount.h"
+#include "gwcontact.h"
+#include "gwcontactproperties.h"
+#include "gwprotocol.h"
+#include "tasks/searchusertask.h"
+
+#include "gwsearch.h"
+
+class GWSearchResultsLVI : public QListViewItem
+{
+public:
+ GWSearchResultsLVI( QListView * parent, GroupWise::ContactDetails details, int status, const QPixmap & statusPM/*, const QString & userId */)
+ : QListViewItem( parent, QString::null, details.givenName, details.surname, GroupWiseProtocol::protocol()->dnToDotted( details.dn ) ), m_details( details ), m_status( status )
+ {
+ setPixmap( 0, statusPM );
+ }
+ QString key( int column, bool ascending ) const
+ {
+ if ( column == 0 )
+ return QString::number( 99 - m_status );
+ else
+ return QListViewItem::key( column, ascending );
+ }
+ GroupWise::ContactDetails m_details;
+ int m_status;
+};
+
+GroupWiseContactSearch::GroupWiseContactSearch( GroupWiseAccount * account, QListView::SelectionMode mode, bool onlineOnly, QWidget *parent, const char *name)
+ : GroupWiseContactSearchWidget(parent, name), m_account( account ), m_onlineOnly( onlineOnly )
+{
+ m_results->setSelectionMode( mode );
+ m_results->setAllColumnsShowFocus( true );
+ connect( m_details, SIGNAL( clicked() ), SLOT( slotShowDetails() ) );
+ connect( m_results, SIGNAL( selectionChanged() ), SLOT( slotValidateSelection() ) );
+ connect( m_search, SIGNAL( clicked() ), SLOT( slotDoSearch() ) );
+ connect( m_clear, SIGNAL( clicked() ), SLOT( slotClear() ) );
+}
+
+
+GroupWiseContactSearch::~GroupWiseContactSearch()
+{
+}
+
+void GroupWiseContactSearch::slotClear()
+{
+ m_firstName->clear();
+ m_lastName->clear();
+ m_userId->clear();
+ m_title->clear();
+ m_dept->clear();
+}
+
+void GroupWiseContactSearch::slotDoSearch()
+{
+ // build a query
+ QValueList< GroupWise::UserSearchQueryTerm > searchTerms;
+ if ( !m_firstName->text().isEmpty() )
+ {
+ GroupWise::UserSearchQueryTerm arg;
+ arg.argument = m_firstName->text();
+ arg.field = "Given Name";
+ arg.operation = searchOperation( m_firstNameOperation->currentItem() );
+ searchTerms.append( arg );
+ }
+ if ( !m_lastName->text().isEmpty() )
+ {
+ GroupWise::UserSearchQueryTerm arg;
+ arg.argument = m_lastName->text();
+ arg.field = "Surname";
+ arg.operation = searchOperation( m_lastNameOperation->currentItem() );
+ searchTerms.append( arg );
+ }
+ if ( !m_userId->text().isEmpty() )
+ {
+ GroupWise::UserSearchQueryTerm arg;
+ arg.argument = m_userId->text();
+ arg.field = NM_A_SZ_USERID;
+ arg.operation = searchOperation( m_userIdOperation->currentItem() );
+ searchTerms.append( arg );
+ }
+ if ( !m_title->text().isEmpty() )
+ {
+ GroupWise::UserSearchQueryTerm arg;
+ arg.argument = m_title->text();
+ arg.field = NM_A_SZ_TITLE;
+ arg.operation = searchOperation( m_titleOperation->currentItem() );
+ searchTerms.append( arg );
+ }
+ if ( !m_dept->text().isEmpty() )
+ {
+ GroupWise::UserSearchQueryTerm arg;
+ arg.argument = m_dept->text();
+ arg.field = NM_A_SZ_DEPARTMENT;
+ arg.operation = searchOperation( m_deptOperation->currentItem() );
+ searchTerms.append( arg );
+ }
+ if ( !searchTerms.isEmpty() )
+ {
+ // start a search task
+ SearchUserTask * st = new SearchUserTask( m_account->client()->rootTask() );
+ st->search( searchTerms );
+ connect( st, SIGNAL( finished() ), SLOT( slotGotSearchResults() ) );
+ st->go( true );
+ m_matchCount->setText( i18n( "Searching" ) );
+ }
+ else
+ {
+ kdDebug( GROUPWISE_DEBUG_GLOBAL ) << k_funcinfo << "no query to perform!" << endl;
+ }
+
+}
+
+void GroupWiseContactSearch::slotShowDetails()
+{
+ kdDebug( GROUPWISE_DEBUG_GLOBAL ) << k_funcinfo << endl;
+ // get the first selected result
+ QValueList< ContactDetails > selected = selectedResults();
+ if ( selected.count() )
+ {
+ // if they are already in our contact list, show that version
+ ContactDetails dt = selected.first();
+ GroupWiseContact * c = m_account->contactForDN( dt.dn );
+ if ( c )
+ new GroupWiseContactProperties( c, this, "gwcontactproperties" );
+ else
+ new GroupWiseContactProperties( dt, this, "gwcontactproperties" );
+ }
+}
+
+void GroupWiseContactSearch::slotGotSearchResults()
+{
+ kdDebug( GROUPWISE_DEBUG_GLOBAL ) << k_funcinfo << endl;
+ SearchUserTask * st = ( SearchUserTask * ) sender();
+ m_searchResults = st->results();
+
+ m_matchCount->setText( i18n( "1 matching user found", "%n matching users found", m_searchResults.count() ) );
+
+ m_results->clear();
+ QValueList< GroupWise::ContactDetails >::Iterator it = m_searchResults.begin();
+ QValueList< GroupWise::ContactDetails >::Iterator end = m_searchResults.end();
+ for ( ; it != end; ++it )
+ {
+ // it's necessary to change the status used for the LVIs,
+ // because the status returned by the server does not go linearly from Unknown to Available
+ // which is no use for us to sort on, and converting it to a Kopete::OnlineStatus is overkill here
+ int statusOrdered;
+ switch ( (*it).status )
+ {
+ case 0: //unknown
+ statusOrdered = 0;
+ break;
+ case 1: //offline
+ statusOrdered = 1;
+ break;
+ case 2: //online
+ statusOrdered = 5;
+ break;
+ case 3: //busy
+ statusOrdered = 2;
+ break;
+ case 4: // away
+ statusOrdered = 3;
+ break;
+ case 5: //idle
+ statusOrdered = 4;
+ break;
+ default:
+ statusOrdered = 0;
+ break;
+ }
+
+ new GWSearchResultsLVI( m_results, *it, statusOrdered,
+ m_account->protocol()->gwStatusToKOS( (*it).status ).iconFor( m_account ) );
+ }
+ // if there was only one hit, select it
+ if ( m_results->childCount() == 1 )
+ m_results->firstChild()->setSelected( true );
+
+ slotValidateSelection();
+}
+
+QValueList< GroupWise::ContactDetails > GroupWiseContactSearch::selectedResults()
+{
+ QValueList< GroupWise::ContactDetails > lst;
+ QListViewItemIterator it( m_results );
+ while ( it.current() ) {
+ if ( it.current()->isSelected() )
+ lst.append( static_cast< GWSearchResultsLVI * >( it.current() )->m_details );
+ ++it;
+ }
+ return lst;
+}
+// GWSearchResultsLVI * selection = static_cast< GWSearchResultsLVI * >( m_results->selectedItem() );
+// contactId = selection->m_dn;
+// if ( displayName.isEmpty() )
+// displayName = selection->text( 1 ) + " " + selection->text( 3 );
+
+
+unsigned char GroupWiseContactSearch::searchOperation( int comboIndex )
+{
+ switch ( comboIndex )
+ {
+ case 0:
+ return NMFIELD_METHOD_SEARCH;
+ case 1:
+ return NMFIELD_METHOD_MATCHBEGIN;
+ case 2:
+ return NMFIELD_METHOD_EQUAL;
+ }
+ return NMFIELD_METHOD_IGNORE;
+}
+
+void GroupWiseContactSearch::slotValidateSelection()
+{
+ bool ok = false;
+ // if we only allow online contacts to be selected
+ if ( m_onlineOnly )
+ {
+ // check that one of the selected items is online
+ QListViewItemIterator it( m_results );
+ while ( it.current() )
+ {
+ if ( it.current()->isSelected() &&
+ !( static_cast< GWSearchResultsLVI * >( it.current() )->m_status == 1 ) )
+ {
+ ok = true;
+ break;
+ }
+ ++it;
+ }
+ }
+ else
+ {
+ // check that at least one item is selected
+ QListViewItemIterator it( m_results );
+ while ( it.current() )
+ {
+ if ( it.current()->isSelected() )
+ {
+ ok = true;
+ break;
+ }
+ ++it;
+ }
+ }
+
+ emit selectionValidates( ok );
+}
+
+#include "gwsearch.moc"
diff --git a/kopete/protocols/groupwise/ui/gwsearch.h b/kopete/protocols/groupwise/ui/gwsearch.h
new file mode 100644
index 00000000..83ee205e
--- /dev/null
+++ b/kopete/protocols/groupwise/ui/gwsearch.h
@@ -0,0 +1,58 @@
+/*
+ Kopete Groupwise Protocol
+ gwsearch.h - logic for server side search widget
+
+ Copyright (c) 2004 SUSE Linux AG http://www.suse.com
+
+ Kopete (c) 2002-2004 by the Kopete developers <[email protected]>
+
+ *************************************************************************
+ * *
+ * This library 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 GWSEARCH_H
+#define GWSEARCH_H
+#include <qlistview.h>
+#include "gwcontactsearch.h"
+
+class GroupWiseAccount;
+class GroupWiseContactProperties;
+class GroupWiseContactSearchWidget;
+
+/**
+Logic for searching for and displaying users and chat rooms using a GroupWiseContactSearchWidget
+
+@author SUSE Linux Products GmbH
+*/
+class GroupWiseContactSearch : public GroupWiseContactSearchWidget
+{
+Q_OBJECT
+public:
+ GroupWiseContactSearch( GroupWiseAccount * account, QListView::SelectionMode mode, bool onlineOnly,
+ QWidget *parent = 0, const char *name = 0);
+ ~GroupWiseContactSearch();
+ QValueList< GroupWise::ContactDetails > selectedResults();
+signals:
+ void selectionValidates( bool );
+protected:
+ unsigned char searchOperation( int comboIndex );
+protected slots:
+ void slotClear();
+ void slotDoSearch();
+ void slotGotSearchResults();
+ // shows a GroupWiseContactProperties for the selected contact. Dialog's parent is this instance
+ void slotShowDetails();
+ void slotValidateSelection();
+private:
+ QValueList< GroupWise::ContactDetails > m_searchResults;
+ GroupWiseAccount * m_account;
+ bool m_onlineOnly;
+};
+
+#endif
diff --git a/kopete/protocols/groupwise/ui/gwshowinvitation.ui b/kopete/protocols/groupwise/ui/gwshowinvitation.ui
new file mode 100644
index 00000000..28dd1a7d
--- /dev/null
+++ b/kopete/protocols/groupwise/ui/gwshowinvitation.ui
@@ -0,0 +1,135 @@
+<!DOCTYPE UI><UI version="3.3" stdsetdef="1">
+<class>ShowInvitationWidget</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>ShowInvitationWidget</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>495</width>
+ <height>204</height>
+ </rect>
+ </property>
+ <property name="caption">
+ <string></string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout13</cstring>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>textLabel1</cstring>
+ </property>
+ <property name="text">
+ <string>&lt;p align="right"&gt;From:&lt;/p&gt;</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>textLabel3</cstring>
+ </property>
+ <property name="text">
+ <string>&lt;p align="right"&gt;Sent:&lt;/p&gt;</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="1">
+ <property name="name">
+ <cstring>m_dateTime</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>5</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>2</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>INVITE_DATE_TIME</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="1">
+ <property name="name">
+ <cstring>m_contactName</cstring>
+ </property>
+ <property name="text">
+ <string>CONTACT_NAME</string>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>m_message</cstring>
+ </property>
+ <property name="frameShape">
+ <enum>Panel</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>Sunken</enum>
+ </property>
+ <property name="text">
+ <string>INVITE_MESSAGE</string>
+ </property>
+ <property name="alignment">
+ <set>WordBreak|AlignVCenter</set>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout14</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel6</cstring>
+ </property>
+ <property name="text">
+ <string>Would you like to join the conversation?</string>
+ </property>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer8</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>31</height>
+ </size>
+ </property>
+ </spacer>
+ </hbox>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>cb_dontShowAgain</cstring>
+ </property>
+ <property name="text">
+ <string>A&amp;lways accept invitations</string>
+ </property>
+ </widget>
+ </vbox>
+</widget>
+<layoutdefaults spacing="6" margin="11"/>
+</UI>